Decompiled source of MoonforgedEventManager v1.0.0

MoonforgedEventManager.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace MoonforgedEventManager;

public sealed class BannerClothMotion : MonoBehaviour
{
	private static readonly Vector4[] players = (Vector4[])(object)new Vector4[16];

	private static readonly int playerData = Shader.PropertyToID("_MoonforgedClothPlayers");

	private static readonly int playerCount = Shader.PropertyToID("_MoonforgedClothPlayerCount");

	private static readonly int enabledId = Shader.PropertyToID("_ClothEnabled");

	private static readonly int bottomId = Shader.PropertyToID("_ClothBottom");

	private static readonly int topId = Shader.PropertyToID("_ClothTop");

	private static int lastFrame = -1;

	private void Start()
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_016e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01db: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		MaterialPropertyBlock val = new MaterialPropertyBlock();
		Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
		Vector3 val5 = default(Vector3);
		foreach (Renderer val2 in componentsInChildren)
		{
			if (((Object)val2).name != "Banner_Cloth")
			{
				continue;
			}
			Mesh val3 = null;
			SkinnedMeshRenderer val4 = (SkinnedMeshRenderer)(object)((val2 is SkinnedMeshRenderer) ? val2 : null);
			if (Object.op_Implicit((Object)(object)val4))
			{
				val3 = val4.sharedMesh;
			}
			else if (val2 is MeshRenderer)
			{
				MeshFilter component = ((Component)val2).GetComponent<MeshFilter>();
				if (Object.op_Implicit((Object)(object)component))
				{
					val3 = component.sharedMesh;
				}
			}
			if (!Object.op_Implicit((Object)(object)val3))
			{
				continue;
			}
			Bounds bounds = val3.bounds;
			bool flag = false;
			Material[] sharedMaterials = val2.sharedMaterials;
			for (int j = 0; j < sharedMaterials.Length; j++)
			{
				if (Object.op_Implicit((Object)(object)sharedMaterials[j]) && sharedMaterials[j].HasProperty(enabledId))
				{
					val.Clear();
					val2.GetPropertyBlock(val, j);
					val.SetFloat(enabledId, 1f);
					val.SetFloat(bottomId, ((Bounds)(ref bounds)).min.y);
					val.SetFloat(topId, ((Bounds)(ref bounds)).max.y);
					val2.SetPropertyBlock(val, j);
					flag = true;
				}
			}
			if (flag)
			{
				Vector3 lossyScale = ((Component)val2).transform.lossyScale;
				((Vector3)(ref val5))..ctor(3f / Mathf.Max(Mathf.Abs(lossyScale.x), 0.001f), 3f / Mathf.Max(Mathf.Abs(lossyScale.y), 0.001f), 3f / Mathf.Max(Mathf.Abs(lossyScale.z), 0.001f));
				((Bounds)(ref bounds)).Expand(val5);
				val2.localBounds = bounds;
			}
		}
	}

	private void LateUpdate()
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: 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_0095: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: 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)
		//IL_00cb: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: 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_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: 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_016b: 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_0171: Unknown result type (might be due to invalid IL or missing references)
		if (lastFrame == Time.frameCount)
		{
			return;
		}
		lastFrame = Time.frameCount;
		int num = 0;
		List<Player> allPlayers = Player.GetAllPlayers();
		Vector3 val = (Object.op_Implicit((Object)(object)Player.m_localPlayer) ? ((Component)Player.m_localPlayer).transform.position : Vector3.zero);
		foreach (Player item in allPlayers)
		{
			if (!Object.op_Implicit((Object)(object)item) || !((Component)item).gameObject.activeInHierarchy)
			{
				continue;
			}
			Vector3 val2 = ((Component)item).transform.position;
			CapsuleCollider component = ((Component)item).GetComponent<CapsuleCollider>();
			float num2 = 1.8f;
			if (Object.op_Implicit((Object)(object)component))
			{
				Bounds bounds = ((Collider)component).bounds;
				val2 = ((Bounds)(ref bounds)).center;
				bounds = ((Collider)component).bounds;
				val2.y = ((Bounds)(ref bounds)).min.y;
				bounds = ((Collider)component).bounds;
				num2 = ((Bounds)(ref bounds)).size.y;
			}
			int num3 = num;
			if (num == players.Length)
			{
				float num4 = -1f;
				num3 = 0;
				Vector3 val3;
				for (int i = 0; i < num; i++)
				{
					val3 = Vector4.op_Implicit(players[i]) - val;
					float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
					if (sqrMagnitude > num4)
					{
						num4 = sqrMagnitude;
						num3 = i;
					}
				}
				val3 = val2 - val;
				if (((Vector3)(ref val3)).sqrMagnitude >= num4)
				{
					continue;
				}
			}
			else
			{
				num++;
			}
			players[num3] = new Vector4(val2.x, val2.y, val2.z, num2);
		}
		Shader.SetGlobalVectorArray(playerData, players);
		Shader.SetGlobalInt(playerCount, num);
	}
}
public sealed class BannerNightEmission : MonoBehaviour
{
	private sealed class Target
	{
		internal Renderer Renderer;

		internal int Index;

		internal Color Color;

		internal bool HasIntensity;

		internal float Intensity;
	}

	private static readonly HashSet<string> MaterialNames = new HashSet<string>
	{
		"Finish", "Start", "M_EM_Danger_Troll", "M_EM_Down", "M_EM_Left", "M_EM_Right", "M_EM_Safe_Haven", "M_EM_Turn_Around", "M_EM_Up", "M_EV_Other_Danger",
		"M_EM_Other_Danger"
	};

	private readonly List<Target> targets = new List<Target>();

	private readonly MaterialPropertyBlock block = new MaterialPropertyBlock();

	private bool initialized;

	private bool lastNight;

	private float nextCheck;

	private void Start()
	{
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val in componentsInChildren)
		{
			Material[] sharedMaterials = val.sharedMaterials;
			for (int j = 0; j < sharedMaterials.Length; j++)
			{
				Material val2 = sharedMaterials[j];
				if (Object.op_Implicit((Object)(object)val2))
				{
					string text = ((Object)val2).name;
					if (text.EndsWith(" (Instance)", StringComparison.Ordinal))
					{
						text = text.Substring(0, text.Length - " (Instance)".Length);
					}
					if (MaterialNames.Contains(text) && val2.HasProperty("_EmissionColor"))
					{
						bool flag = val2.HasProperty("_EmissionIntensity");
						targets.Add(new Target
						{
							Renderer = val,
							Index = j,
							Color = val2.GetColor("_EmissionColor"),
							HasIntensity = flag,
							Intensity = (flag ? val2.GetFloat("_EmissionIntensity") : 1f)
						});
					}
				}
			}
		}
		Update();
	}

	private void Update()
	{
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)EnvMan.instance) || Time.time < nextCheck)
		{
			return;
		}
		nextCheck = Time.time + 1f;
		bool flag = EnvMan.IsNight();
		if (initialized && flag == lastNight)
		{
			return;
		}
		initialized = true;
		lastNight = flag;
		foreach (Target target in targets)
		{
			if (Object.op_Implicit((Object)(object)target.Renderer))
			{
				block.Clear();
				target.Renderer.GetPropertyBlock(block, target.Index);
				block.SetColor("_EmissionColor", flag ? target.Color : Color.black);
				if (target.HasIntensity)
				{
					block.SetFloat("_EmissionIntensity", flag ? target.Intensity : 0f);
				}
				target.Renderer.SetPropertyBlock(block, target.Index);
			}
		}
	}
}
public class BannerServerText : MonoBehaviour
{
	private static readonly HashSet<BannerServerText> activeBanners = new HashSet<BannerServerText>();

	private TMP_Text serverNameText;

	private void Awake()
	{
		FindText();
	}

	private void OnEnable()
	{
		activeBanners.Add(this);
		ApplySettings();
	}

	private void Start()
	{
		ApplySettings();
	}

	private void OnDisable()
	{
		activeBanners.Remove(this);
	}

	private void FindText()
	{
		TMP_Text[] componentsInChildren = ((Component)this).GetComponentsInChildren<TMP_Text>(true);
		TMP_Text[] array = componentsInChildren;
		foreach (TMP_Text val in array)
		{
			if (((Object)val).name == "ServerName_Text")
			{
				serverNameText = val;
				return;
			}
		}
		if (((Object)((Component)this).gameObject).name.StartsWith("M_EM_Hanging_Banner"))
		{
			TMP_Text[] array2 = componentsInChildren;
			foreach (TMP_Text val2 in array2)
			{
				if (((Object)val2).name == "Text (TMP)")
				{
					serverNameText = val2;
					return;
				}
			}
		}
		ManualLogSource modLogger = MoonforgedEventManager.ModLogger;
		if (modLogger != null)
		{
			modLogger.LogWarning((object)("[Moonforged Event Manager] ServerName_Text not found on " + ((Object)((Component)this).gameObject).name + "."));
		}
	}

	public void ApplySettings()
	{
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)serverNameText == (Object)null)
		{
			FindText();
		}
		if (!((Object)(object)serverNameText == (Object)null) && MoonforgedEventManager.ShowServerName != null)
		{
			((Behaviour)serverNameText).enabled = MoonforgedEventManager.ShowServerName.Value;
			serverNameText.text = MoonforgedEventManager.ServerNameText.Value;
			serverNameText.fontSize = MoonforgedEventManager.ServerNameFontSize.Value;
			Color color = default(Color);
			if (ColorUtility.TryParseHtmlString(MoonforgedEventManager.ServerNameColor.Value, ref color))
			{
				((Graphic)serverNameText).color = color;
			}
			if (((Object)((Component)this).gameObject).name.StartsWith("M_EM_Hanging_Banner"))
			{
				serverNameText.transform.localPosition = new Vector3(MoonforgedEventManager.ServerNamePosX.Value, MoonforgedEventManager.ServerNamePosY.Value, MoonforgedEventManager.ServerNamePosZ.Value);
			}
		}
	}

	public static void RefreshAll()
	{
		foreach (BannerServerText activeBanner in activeBanners)
		{
			if ((Object)(object)activeBanner != (Object)null)
			{
				activeBanner.ApplySettings();
			}
		}
	}
}
public class DeathScoreboard : MonoBehaviour
{
	private TMP_Text scoreText;

	private readonly TMP_Text[] columns = (TMP_Text[])(object)new TMP_Text[3];

	private readonly TMP_Text[] glowColumns = (TMP_Text[])(object)new TMP_Text[3];

	private readonly List<Material> materials = new List<Material>();

	private Material separatorMaterial;

	private Material carvedMaterial;

	private Material blueMaterial;

	private readonly TMP_Text[] rankCells = (TMP_Text[])(object)new TMP_Text[30];

	private readonly TMP_Text[] nameCells = (TMP_Text[])(object)new TMP_Text[30];

	private readonly TMP_Text[] countCells = (TMP_Text[])(object)new TMP_Text[30];

	private readonly TMP_Text[] blueNameCells = (TMP_Text[])(object)new TMP_Text[30];

	private void Awake()
	{
		//IL_0007: 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_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: 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_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: 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_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: 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_017e: Unknown result type (might be due to invalid IL or missing references)
		//IL_03df: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0404: Expected O, but got Unknown
		//IL_0475: Unknown result type (might be due to invalid IL or missing references)
		//IL_0485: Unknown result type (might be due to invalid IL or missing references)
		//IL_04a6: Unknown result type (might be due to invalid IL or missing references)
		Scene scene = ((Component)this).gameObject.scene;
		if (!((Scene)(ref scene)).IsValid())
		{
			return;
		}
		Transform val = ((Component)this).transform.Find("ScoreText");
		scoreText = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TMP_Text>() : ((Component)this).GetComponentInChildren<TMP_Text>(true));
		if ((Object)(object)scoreText == (Object)null)
		{
			return;
		}
		TMP_FontAsset val2 = Resources.Load<TMP_FontAsset>("fonts & materials/valheim_fonts/Valheim-Norsebold");
		if ((Object)(object)val2 == (Object)null)
		{
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			foreach (TMP_FontAsset val3 in array)
			{
				if (((Object)val3).name.ToLowerInvariant().Contains("norsebold"))
				{
					val2 = val3;
					break;
				}
			}
		}
		if ((Object)(object)val2 != (Object)null)
		{
			scoreText.font = val2;
		}
		RectTransform rectTransform = scoreText.rectTransform;
		Vector4 margin = scoreText.margin;
		float x = ((Transform)rectTransform).localPosition.x;
		Rect rect = rectTransform.rect;
		float num = x + ((Rect)(ref rect)).xMin + margin.x;
		float x2 = ((Transform)rectTransform).localPosition.x;
		rect = rectTransform.rect;
		float num2 = x2 + ((Rect)(ref rect)).xMax - margin.z;
		float y = ((Transform)rectTransform).localPosition.y;
		rect = rectTransform.rect;
		float num3 = y + ((Rect)(ref rect)).yMax - margin.y;
		float y2 = ((Transform)rectTransform).localPosition.y;
		rect = rectTransform.rect;
		float num4 = y2 + ((Rect)(ref rect)).yMin + margin.w;
		float num5 = (num2 - num) / 3f;
		float fontSize = scoreText.fontSize;
		scoreText.text = "";
		for (int j = 0; j < 3; j++)
		{
			columns[j] = MakeText("CarvedNames_" + j, num + (float)j * num5 + num5 * 0.025f, num5 * 0.95f, num3, num4, fontSize, glow: false);
			glowColumns[j] = MakeText("BlueNames_" + j, num + (float)j * num5 + num5 * 0.025f, num5 * 0.95f, num3, num4, fontSize, glow: true);
		}
		float num6 = (num3 - num4) / 10f;
		for (int k = 0; k < 30; k++)
		{
			int num7 = k / 10;
			int num8 = k % 10;
			float num9 = num + (float)num7 * num5 + num5 * 0.025f;
			float top = num3 - (float)num8 * num6;
			float num10 = num5 * 0.95f;
			rankCells[k] = MakeCell("DeathRank_" + k, num9, num10 * 0.13f, top, num6, fontSize, glow: false);
			nameCells[k] = MakeCell("DeathName_" + k, num9 + num10 * 0.15f, num10 * 0.61f, top, num6, fontSize, glow: false);
			countCells[k] = MakeCell("DeathCount_" + k, num9 + num10 * 0.8f, num10 * 0.19f, top, num6, fontSize, glow: false);
			blueNameCells[k] = MakeCell("BlueDeathName_" + k, num9 + num10 * 0.15f, num10 * 0.61f, top, num6, fontSize, glow: true);
			countCells[k].alignment = (TextAlignmentOptions)4100;
		}
		ShowCells(show: false);
		Shader val4 = Shader.Find("Unlit/Color") ?? Shader.Find("Sprites/Default");
		if ((Object)(object)val4 != (Object)null)
		{
			separatorMaterial = new Material(val4)
			{
				color = new Color(0.16f, 0.17f, 0.18f)
			};
			for (int l = 1; l < 3; l++)
			{
				GameObject val5 = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)val5).name = "CarvedColumnSeparator_" + l;
				Object.Destroy((Object)(object)val5.GetComponent<Collider>());
				val5.transform.SetParent(scoreText.transform.parent, false);
				val5.transform.localPosition = new Vector3(num + num5 * (float)l, (num3 + num4) / 2f, ((Transform)rectTransform).localPosition.z - 0.018f);
				val5.transform.localScale = new Vector3(0.018f, num3 - num4, 0.003f);
				val5.GetComponent<Renderer>().sharedMaterial = separatorMaterial;
			}
		}
	}

	private TMP_Text MakeText(string name, float left, float width, float top, float bottom, float font, bool glow)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: 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)
		//IL_0107: 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_0126: 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_018f: Expected O, but got Unknown
		//IL_020e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0220: Unknown result type (might be due to invalid IL or missing references)
		//IL_0395: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = Object.Instantiate<GameObject>(((Component)scoreText).gameObject, scoreText.transform.parent);
		((Object)val).name = name;
		TMP_Text component = val.GetComponent<TMP_Text>();
		component.text = "";
		component.margin = Vector4.zero;
		component.alignment = (TextAlignmentOptions)257;
		component.enableAutoSizing = false;
		component.fontSize = font;
		component.fontStyle = (FontStyles)16;
		component.richText = true;
		component.textWrappingMode = (TextWrappingModes)0;
		component.overflowMode = (TextOverflowModes)3;
		component.lineSpacing = 0f;
		RectTransform rectTransform = component.rectTransform;
		rectTransform.pivot = new Vector2(0.5f, 0.5f);
		rectTransform.sizeDelta = new Vector2(width, Mathf.Max(0.1f, top - bottom));
		((Transform)rectTransform).localPosition = new Vector3(left + width / 2f, (top + bottom) / 2f, ((Transform)scoreText.rectTransform).localPosition.z - (glow ? 0.012f : 0.01f));
		((Graphic)component).color = (glow ? new Color(0.15f, 0.65f, 1f) : new Color(0.12f, 0.13f, 0.14f));
		Material val2 = (glow ? blueMaterial : carvedMaterial);
		if ((Object)(object)val2 != (Object)null)
		{
			component.fontSharedMaterial = val2;
			return component;
		}
		Material val3 = new Material(((TMP_Asset)scoreText.font).material);
		Shader val4 = Shader.Find("TextMeshPro/Distance Field");
		if ((Object)(object)val4 != (Object)null)
		{
			val3.shader = val4;
		}
		if (glow)
		{
			blueMaterial = val3;
		}
		else
		{
			carvedMaterial = val3;
		}
		materials.Add(val3);
		component.fontSharedMaterial = val3;
		((Graphic)component).color = (glow ? new Color(0.15f, 0.65f, 1f) : new Color(0.12f, 0.13f, 0.14f));
		val3.SetColor("_FaceColor", Color.white);
		Set(val3, "_FaceDilate", 0f);
		if (glow)
		{
			val3.DisableKeyword("UNDERLAY_ON");
			val3.EnableKeyword("GLOW_ON");
			Set(val3, "_GlowPower", 0.65f);
			Set(val3, "_GlowOuter", 0.35f);
			if (val3.HasProperty("_GlowColor"))
			{
				val3.SetColor("_GlowColor", new Color(0.1f, 0.5f, 1.5f, 1f));
			}
		}
		else
		{
			val3.DisableKeyword("GLOW_ON");
			val3.EnableKeyword("UNDERLAY_ON");
			val3.EnableKeyword("BEVEL_ON");
			Set(val3, "_Bevel", 0.2f);
			Set(val3, "_BevelWidth", 0.3f);
			Set(val3, "_BevelOffset", -0.15f);
			Set(val3, "_LightAngle", 3.14f);
			Set(val3, "_Ambient", 0.6f);
			Set(val3, "_SpecularPower", 0f);
			Set(val3, "_OutlineWidth", 0.035f);
			if (val3.HasProperty("_OutlineColor"))
			{
				val3.SetColor("_OutlineColor", new Color(0.34f, 0.36f, 0.38f, 0.7f));
			}
			if (val3.HasProperty("_UnderlayColor"))
			{
				val3.SetColor("_UnderlayColor", new Color(0.38f, 0.4f, 0.42f, 0.6f));
			}
			Set(val3, "_UnderlayOffsetX", -0.3f);
			Set(val3, "_UnderlayOffsetY", 0.3f);
			Set(val3, "_UnderlaySoftness", 0.08f);
		}
		return component;
	}

	private TMP_Text MakeCell(string name, float x, float width, float top, float height, float font, bool glow)
	{
		TMP_Text val = MakeText(name, x, width, top, top - height, font, glow);
		val.alignment = (TextAlignmentOptions)4097;
		val.enableAutoSizing = true;
		val.fontSizeMax = font * (name.Contains("Name") ? 2f : 2.5f);
		val.fontSizeMin = font * 0.7f;
		val.overflowMode = (TextOverflowModes)1;
		val.richText = false;
		return val;
	}

	private void ShowCells(bool show)
	{
		for (int i = 0; i < 30; i++)
		{
			if (!((Object)(object)rankCells[i] == (Object)null))
			{
				((Component)rankCells[i]).gameObject.SetActive(show);
				((Component)nameCells[i]).gameObject.SetActive(show);
				((Component)countCells[i]).gameObject.SetActive(show);
				((Component)blueNameCells[i]).gameObject.SetActive(show);
			}
		}
	}

	private static void Set(Material mat, string key, float value)
	{
		if (mat.HasProperty(key))
		{
			mat.SetFloat(key, value);
		}
	}

	private void OnEnable()
	{
		//IL_0007: 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)
		Scene scene = ((Component)this).gameObject.scene;
		if (((Scene)(ref scene)).IsValid())
		{
			DeathTracker.Instance?.RegisterBoard(this);
		}
	}

	private void Start()
	{
		//IL_0007: 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)
		Scene scene = ((Component)this).gameObject.scene;
		if (((Scene)(ref scene)).IsValid())
		{
			DeathTracker.Instance?.RegisterBoard(this);
		}
	}

	private void OnDisable()
	{
		DeathTracker.Instance?.UnregisterBoard(this);
	}

	private void OnDestroy()
	{
		foreach (Material material in materials)
		{
			Object.Destroy((Object)(object)material);
		}
		if ((Object)(object)separatorMaterial != (Object)null)
		{
			Object.Destroy((Object)(object)separatorMaterial);
		}
	}

	public void SetBoardText(string text)
	{
		if (!((Object)(object)columns[0] == (Object)null))
		{
			ShowCells(show: false);
			TMP_Text[] array = columns;
			foreach (TMP_Text val in array)
			{
				((Component)val).gameObject.SetActive(true);
			}
			columns[0].text = text;
			for (int j = 1; j < 3; j++)
			{
				columns[j].text = "";
			}
			TMP_Text[] array2 = glowColumns;
			foreach (TMP_Text val2 in array2)
			{
				val2.text = "";
				((Component)val2).gameObject.SetActive(true);
			}
		}
	}

	internal void SetRows(List<DeathStoneRow> rows, int start, Func<long, bool> glowing)
	{
		if ((Object)(object)columns[0] == (Object)null)
		{
			return;
		}
		TMP_Text[] array = columns;
		foreach (TMP_Text val in array)
		{
			((Component)val).gameObject.SetActive(false);
		}
		TMP_Text[] array2 = glowColumns;
		foreach (TMP_Text val2 in array2)
		{
			((Component)val2).gameObject.SetActive(false);
		}
		ShowCells(show: true);
		for (int k = 0; k < 30; k++)
		{
			int num = start + k;
			bool flag = num < rows.Count;
			rankCells[k].text = (flag ? (num + 1 + ".") : "");
			nameCells[k].text = (flag ? DeathTracker.Clean(rows[num].Name) : "");
			countCells[k].text = (flag ? rows[num].Deaths.ToString() : "");
			bool flag2 = flag && glowing(rows[num].Id);
			nameCells[k].alpha = (flag2 ? 0f : 1f);
			blueNameCells[k].text = (flag2 ? nameCells[k].text : "");
			if (flag2)
			{
				nameCells[k].ForceMeshUpdate(false, false);
				blueNameCells[k].enableAutoSizing = false;
				blueNameCells[k].fontSize = nameCells[k].fontSize;
			}
		}
	}

	internal void Strike()
	{
		//IL_0007: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Expected O, but got Unknown
		//IL_00ee: 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_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)
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: 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_0290: Unknown result type (might be due to invalid IL or missing references)
		//IL_0295: Unknown result type (might be due to invalid IL or missing references)
		Scene scene = ((Component)this).gameObject.scene;
		if (!((Scene)(ref scene)).IsValid())
		{
			return;
		}
		GameObject val = PrefabManager.Instance.GetPrefab("lightningAOE");
		if ((Object)(object)val == (Object)null)
		{
			GameObject prefab = PrefabManager.Instance.GetPrefab("incinerator");
			Incinerator val2 = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Incinerator>() : null);
			if ((Object)(object)val2 != (Object)null)
			{
				object? obj = AccessTools.Field(typeof(Incinerator), "m_lightingAOEs")?.GetValue(val2);
				val = (GameObject)((obj is GameObject) ? obj : null);
			}
		}
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		GameObject val3 = new GameObject("Death stone lightning");
		val3.SetActive(false);
		val3.transform.position = (((Object)(object)scoreText != (Object)null) ? scoreText.transform.position : ((Component)this).transform.position);
		List<ParticleSystem> list = new List<ParticleSystem>();
		ParticleSystem[] componentsInChildren = val.GetComponentsInChildren<ParticleSystem>(true);
		foreach (ParticleSystem val4 in componentsInChildren)
		{
			bool flag = false;
			Transform parent = ((Component)val4).transform.parent;
			while ((Object)(object)parent != (Object)null && (Object)(object)parent != (Object)(object)val.transform)
			{
				if ((Object)(object)((Component)parent).GetComponent<ParticleSystem>() != (Object)null)
				{
					flag = true;
					break;
				}
				parent = parent.parent;
			}
			if (!flag)
			{
				GameObject val5 = Object.Instantiate<GameObject>(((Component)val4).gameObject, val3.transform);
				val5.transform.localPosition = val.transform.InverseTransformPoint(((Component)val4).transform.position);
				val5.transform.localRotation = Quaternion.Inverse(val.transform.rotation) * ((Component)val4).transform.rotation;
				MonoBehaviour[] componentsInChildren2 = val5.GetComponentsInChildren<MonoBehaviour>(true);
				foreach (MonoBehaviour val6 in componentsInChildren2)
				{
					Object.DestroyImmediate((Object)(object)val6);
				}
				Collider[] componentsInChildren3 = val5.GetComponentsInChildren<Collider>(true);
				foreach (Collider val7 in componentsInChildren3)
				{
					Object.DestroyImmediate((Object)(object)val7);
				}
				AudioSource[] componentsInChildren4 = val5.GetComponentsInChildren<AudioSource>(true);
				foreach (AudioSource val8 in componentsInChildren4)
				{
					Object.DestroyImmediate((Object)(object)val8);
				}
				ParticleSystem[] componentsInChildren5 = val5.GetComponentsInChildren<ParticleSystem>(true);
				foreach (ParticleSystem val9 in componentsInChildren5)
				{
					((Component)val9).gameObject.SetActive(true);
					MainModule main = val9.main;
					((MainModule)(ref main)).playOnAwake = false;
					((MainModule)(ref main)).loop = false;
					list.Add(val9);
				}
				val5.SetActive(true);
			}
		}
		val3.SetActive(true);
		foreach (ParticleSystem item in list)
		{
			item.Stop(false, (ParticleSystemStopBehavior)0);
			item.Play(false);
		}
		Object.Destroy((Object)(object)val3, 6f);
	}
}
public class DeathSoundGain : MonoBehaviour
{
	private volatile float gain = 1f;

	private AudioSource source;

	private void Awake()
	{
		source = ((Component)this).GetComponent<AudioSource>();
		Apply();
	}

	private void Update()
	{
		Apply();
	}

	private void Apply()
	{
		gain = DeathTracker.DeathGain;
		if ((Object)(object)source != (Object)null)
		{
			source.volume = Mathf.Clamp01(AudioMan.GetSFXVolume());
		}
	}

	private void OnAudioFilterRead(float[] data, int channels)
	{
		float num = gain;
		for (int i = 0; i < data.Length; i++)
		{
			data[i] *= num;
		}
	}
}
internal sealed class DeathStoneRow
{
	internal long Id;

	internal string Name;

	internal int Deaths;
}
internal static class DeathStoneLayout
{
	internal const int PageSize = 30;

	internal static List<DeathStoneRow> Preview()
	{
		string[] array = new string[30]
		{
			"Caen", "Eirik", "Freya", "Bjorn", "Astrid", "Ragnar", "Sigrid", "Ulf", "Thyra", "Leif",
			"Hakon", "Ingrid", "Torsten", "Ylva", "Gunnar", "Solveig", "Ivar", "Runa", "Harald", "Liv",
			"Sten", "Helga", "Arne", "Kari", "Vidar", "Sven", "Dag", "Alva", "Torvi", "Einar"
		};
		Random random = new Random(7241);
		List<DeathStoneRow> list = new List<DeathStoneRow>();
		for (int i = 0; i < array.Length; i++)
		{
			list.Add(new DeathStoneRow
			{
				Id = -i - 1,
				Name = array[i],
				Deaths = random.Next(1, 100)
			});
		}
		list.Sort((DeathStoneRow a, DeathStoneRow b) => b.Deaths.CompareTo(a.Deaths));
		return list;
	}

	internal static string Row(DeathStoneRow row, int rank, bool hideName)
	{
		string text = row.Name.Replace("<", "").Replace(">", "").Replace("\n", " ")
			.Replace("\r", " ");
		if (text.Length > 16)
		{
			text = text.Substring(0, 15) + "…";
		}
		return rank + ". " + (hideName ? ("<alpha=#00>" + text + "<alpha=#FF>") : text) + " <pos=82%>" + row.Deaths;
	}
}
public class DeathTracker : MonoBehaviour
{
	[DataContract]
	private class DeathReport
	{
		[DataMember]
		public string EventId;

		[DataMember]
		public long PlayerId;

		[DataMember]
		public string PlayerName;

		[DataMember]
		public string Killer;

		[DataMember]
		public string Cause;
	}

	[DataContract]
	private class DeathAnnouncement
	{
		[DataMember]
		public long PlayerId;

		[DataMember]
		public bool FirstDeath;

		[DataMember]
		public string PlayerName;

		[DataMember]
		public string Killer;

		[DataMember]
		public string Cause;

		[DataMember]
		public int Deaths;
	}

	[DataContract]
	private class DeathTotals
	{
		[DataMember]
		public List<DeathEntry> Players = new List<DeathEntry>();
	}

	[DataContract]
	private class DeathEntry
	{
		[DataMember]
		public long PlayerId;

		[DataMember]
		public string PlayerName;

		[DataMember]
		public int Deaths;
	}

	internal static DeathTracker Instance;

	internal static ConfigEntry<bool> ShowRaceDeaths;

	private int effectGeneration;

	private static ConfigEntry<bool> PreviewBoard;

	private readonly Dictionary<long, float> firstDeathGlow = new Dictionary<long, float>();

	private static ConfigEntry<bool> Announcements;

	private static ConfigEntry<bool> AnnouncementSound;

	private static ConfigEntry<float> SoundVolume;

	private static ConfigEntry<float> SoundDelay;

	private static ConfigEntry<int> SoundRepeats;

	private readonly List<GameObject> activeDeathSounds = new List<GameObject>();

	private static AudioClip deathClip;

	private const string ReportRpc = "MoonforgedDeaths_Report";

	private const string StateRpc = "MoonforgedDeaths_State";

	private const string RequestRpc = "MoonforgedDeaths_Request";

	private const string AnnouncementRpc = "MoonforgedDeaths_Announce";

	private readonly List<DeathScoreboard> boards = new List<DeathScoreboard>();

	private readonly HashSet<string> received = new HashSet<string>();

	private DeathTotals totals = new DeathTotals();

	private ZRoutedRpc registeredRpc;

	private string filePath;

	private string snapshot;

	private bool canSave;

	private float pageStarted;

	private int page;

	private readonly Queue<string> notices = new Queue<string>();

	private string currentNotice;

	private float noticeUntil;

	private GUIStyle noticeStyle;

	internal static float DeathGain => (SoundVolume == null) ? 1f : (Mathf.Clamp(SoundVolume.Value, 0f, 300f) / 100f);

	internal static void Initialize(ConfigFile config)
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Expected O, but got Unknown
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Expected O, but got Unknown
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Expected O, but got Unknown
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Expected O, but got Unknown
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Expected O, but got Unknown
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Expected O, but got Unknown
		//IL_019b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: Expected O, but got Unknown
		PreviewBoard = config.Bind<bool>("Deaths", "PreviewDeathBoard", false, "Local visual preview: display 30 sample players without changing saved death totals. Turn off to display real players.");
		PreviewBoard.SettingChanged += delegate
		{
			Instance?.RefreshBoards();
		};
		ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		};
		ShowRaceDeaths = config.Bind<bool>("Deaths", "ShowRaceDeaths", true, new ConfigDescription("Show race deaths on the right side of the event scoreboard.", (AcceptableValueBase)null, new object[1] { val }));
		ShowRaceDeaths.SettingChanged += delegate
		{
			MoonforgedTriathlonManager.Instance?.RefreshDeathColumns();
		};
		Announcements = config.Bind<bool>("Deaths", "DeathAnnouncements", true, new ConfigDescription("Announce player deaths and totals at the top of every player's screen.", (AcceptableValueBase)null, new object[1] { val }));
		AnnouncementSound = config.Bind<bool>("Deaths", "DeathSound", true, new ConfigDescription("Play Death Note once per death, including race deaths.", (AcceptableValueBase)null, new object[1] { val }));
		SoundVolume = config.Bind<float>("Deaths", "DeathSoundVolumePercent", 250f, new ConfigDescription("Death announcement gain, independent of the horn. 100 = original, 300 = triple gain; high gain may distort.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), new object[1] { val }));
		SoundDelay = config.Bind<float>("Deaths", "DeathSoundDelaySeconds", 2f, new ConfigDescription("Delay after receiving a death before its sound plays.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 15f), new object[1] { val }));
		SoundRepeats = config.Bind<int>("Deaths", "DeathSoundRepeats", 1, new ConfigDescription("Number of full plays of the death clip. Two plays are about 8 seconds with the current clip.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), new object[1] { val }));
	}

	internal static void Load(AssetBundle bundle)
	{
		deathClip = bundle.LoadAsset<AudioClip>("assets/death_note.ogg") ?? bundle.LoadAsset<AudioClip>("assets/imperium events/horn/materials/death_note.ogg");
	}

	private void Awake()
	{
		Instance = this;
		((MonoBehaviour)this).StartCoroutine(NetworkSession());
	}

	private void OnDestroy()
	{
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	private IEnumerator NetworkSession()
	{
		while (true)
		{
			if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null)
			{
				registeredRpc = null;
				yield return null;
				continue;
			}
			if (registeredRpc != ZRoutedRpc.instance)
			{
				received.Clear();
				firstDeathGlow.Clear();
				foreach (GameObject sound in activeDeathSounds)
				{
					if ((Object)(object)sound != (Object)null)
					{
						Object.Destroy((Object)(object)sound);
					}
				}
				activeDeathSounds.Clear();
				notices.Clear();
				currentNotice = null;
				snapshot = null;
				page = 0;
				pageStarted = Time.unscaledTime;
				totals = new DeathTotals();
				ZRoutedRpc.instance.Register<string>("MoonforgedDeaths_Report", (Action<long, string>)ReceiveDeath);
				ZRoutedRpc.instance.Register<string>("MoonforgedDeaths_State", (Action<long, string>)ReceiveState);
				ZRoutedRpc.instance.Register("MoonforgedDeaths_Request", (Action<long>)SendRequestedState);
				ZRoutedRpc.instance.Register<string>("MoonforgedDeaths_Announce", (Action<long, string>)ReceiveAnnouncement);
				registeredRpc = ZRoutedRpc.instance;
				if (ZNet.instance.IsServer())
				{
					LoadTotals();
				}
				else
				{
					RequestState();
				}
			}
			else if (!ZNet.instance.IsServer() && snapshot == null)
			{
				RequestState();
			}
			if (firstDeathGlow.Count == 0 && Time.unscaledTime - pageStarted >= 10f)
			{
				page++;
				pageStarted = Time.unscaledTime;
				RefreshBoards();
			}
			yield return (object)new WaitForSecondsRealtime(1f);
		}
	}

	internal static string Clean(string value)
	{
		if (string.IsNullOrWhiteSpace(value))
		{
			return "Unknown";
		}
		string text = value.Replace("<", "").Replace(">", "").Replace("\n", " ")
			.Replace("\r", " ")
			.Replace("\u001f", " ")
			.Trim();
		return (text.Length > 48) ? text.Substring(0, 48) : text;
	}

	internal void Report(Player player, HitData hit)
	{
		if ((FeatureSwitches.DeathEnabled || FeatureSwitches.EventEnabled) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && !((Object)(object)ZNet.instance == (Object)null) && ZRoutedRpc.instance != null && registeredRpc != null && registeredRpc == ZRoutedRpc.instance)
		{
			Character val = ((hit != null) ? hit.GetAttacker() : null);
			DeathReport value = new DeathReport
			{
				EventId = Guid.NewGuid().ToString("N"),
				PlayerId = player.GetPlayerID(),
				PlayerName = Clean(player.GetPlayerName()),
				Killer = (((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)player) ? Clean(val.GetHoverName()) : ""),
				Cause = ((hit != null) ? ((object)Unsafe.As<HitType, HitType>(ref hit.m_hitType)/*cast due to .constrained prefix*/).ToString() : "Undefined")
			};
			long num = (ZNet.instance.IsServer() ? ZNet.GetUID() : (ZNet.instance.GetServerPeer()?.m_uid ?? 0));
			if (num != 0)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(num, "MoonforgedDeaths_Report", new object[1] { Serialize(value) });
			}
		}
	}

	private void ReceiveDeath(long sender, string json)
	{
		if ((!FeatureSwitches.DeathEnabled && !FeatureSwitches.EventEnabled) || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || string.IsNullOrEmpty(json) || json.Length > 4096)
		{
			return;
		}
		DeathReport report;
		try
		{
			report = Deserialize<DeathReport>(json);
		}
		catch
		{
			return;
		}
		if (report == null || report.PlayerId == 0L || string.IsNullOrEmpty(report.EventId) || report.EventId.Length != 32)
		{
			return;
		}
		if (sender != ZNet.GetUID())
		{
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			if (peer == null || string.IsNullOrWhiteSpace(peer.m_playerName))
			{
				return;
			}
			report.PlayerName = Clean(peer.m_playerName);
		}
		else if ((Object)(object)Player.m_localPlayer == (Object)null || Player.m_localPlayer.GetPlayerID() != report.PlayerId)
		{
			return;
		}
		string item = sender + ":" + report.EventId;
		if (received.Contains(item))
		{
			return;
		}
		if (received.Count >= 4096)
		{
			received.Clear();
		}
		received.Add(item);
		string playerName = Clean(report.PlayerName);
		MoonforgedTriathlonManager.Instance?.RecordRaceDeath(playerName);
		if (FeatureSwitches.DeathEnabled)
		{
			DeathEntry deathEntry = totals.Players.FirstOrDefault((DeathEntry x) => x.PlayerId == report.PlayerId);
			if (deathEntry == null)
			{
				deathEntry = new DeathEntry
				{
					PlayerId = report.PlayerId,
					PlayerName = playerName
				};
				totals.Players.Add(deathEntry);
			}
			bool firstDeath = deathEntry.Deaths == 0;
			deathEntry.PlayerName = playerName;
			deathEntry.Deaths++;
			SaveTotals();
			string text = Serialize(totals);
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "MoonforgedDeaths_State", new object[1] { text });
			DeathAnnouncement value = new DeathAnnouncement
			{
				PlayerName = playerName,
				Killer = CleanOptional(report.Killer),
				Cause = report.Cause,
				Deaths = deathEntry.Deaths,
				PlayerId = deathEntry.PlayerId,
				FirstDeath = firstDeath
			};
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "MoonforgedDeaths_Announce", new object[1] { Serialize(value) });
		}
	}

	private static string CleanOptional(string value)
	{
		return string.IsNullOrWhiteSpace(value) ? "" : Clean(value);
	}

	private static bool FromServer(long sender)
	{
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			return false;
		}
		int result;
		if (!ZNet.instance.IsServer())
		{
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			result = ((serverPeer != null && serverPeer.m_uid == sender) ? 1 : 0);
		}
		else
		{
			result = ((sender == ZNet.GetUID()) ? 1 : 0);
		}
		return (byte)result != 0;
	}

	private void ReceiveState(long sender, string json)
	{
		if (!FromServer(sender) || string.IsNullOrEmpty(json))
		{
			return;
		}
		try
		{
			DeathTotals deathTotals = Deserialize<DeathTotals>(json);
			if (deathTotals?.Players != null)
			{
				totals = deathTotals;
				snapshot = json;
				RefreshBoards();
			}
		}
		catch
		{
		}
	}

	private void ReceiveAnnouncement(long sender, string json)
	{
		if (!FeatureSwitches.DeathEnabled || !FromServer(sender) || string.IsNullOrEmpty(json) || json.Length > 4096)
		{
			return;
		}
		DeathAnnouncement data;
		try
		{
			data = Deserialize<DeathAnnouncement>(json);
		}
		catch
		{
			return;
		}
		if (data == null)
		{
			return;
		}
		if (data.FirstDeath && data.PlayerId != 0)
		{
			firstDeathGlow[data.PlayerId] = Time.unscaledTime + 15f;
			List<DeathEntry> list = (from x in totals.Players
				orderby x.Deaths descending, x.PlayerName
				select x).ToList();
			int num = list.FindIndex((DeathEntry x) => x.PlayerId == data.PlayerId);
			if (num >= 0)
			{
				page = num / 30;
			}
			pageStarted = Time.unscaledTime;
			RefreshBoards();
			if (!PreviewBoard.Value)
			{
				foreach (DeathScoreboard board in boards)
				{
					if ((Object)(object)board != (Object)null)
					{
						board.Strike();
					}
				}
			}
		}
		string text = CauseText(data);
		if ((Object)(object)Player.m_localPlayer != (Object)null && (Announcements.Value || AnnouncementSound.Value))
		{
			notices.Enqueue(Clean(data.PlayerName) + text + " (Deaths: " + data.Deaths + ")");
			if (string.IsNullOrEmpty(currentNotice))
			{
				noticeUntil = 0f;
			}
			((MonoBehaviour)this).StartCoroutine(PlayDeathSoundAfterDelay(registeredRpc, effectGeneration));
		}
	}

	private IEnumerator PlayDeathSoundAfterDelay(ZRoutedRpc session, int generation)
	{
		yield return (object)new WaitForSecondsRealtime(SoundDelay.Value);
		if (generation == effectGeneration && FeatureSwitches.DeathEnabled && registeredRpc == session && ZRoutedRpc.instance == session && !((Object)(object)Player.m_localPlayer == (Object)null) && AnnouncementSound.Value && !(SoundVolume.Value <= 0f) && !((Object)(object)deathClip == (Object)null))
		{
			GameObject go = new GameObject("Moonforged death announcement sound");
			AudioSource source = go.AddComponent<AudioSource>();
			source.playOnAwake = false;
			source.spatialBlend = 0f;
			source.outputAudioMixerGroup = null;
			source.clip = deathClip;
			int repeats = Mathf.Clamp(SoundRepeats.Value, 1, 5);
			source.loop = repeats > 1;
			go.AddComponent<DeathSoundGain>();
			activeDeathSounds.Add(go);
			source.Play();
			yield return (object)new WaitForSecondsRealtime(deathClip.length * (float)repeats);
			if ((Object)(object)go != (Object)null)
			{
				Object.Destroy((Object)(object)go);
			}
			activeDeathSounds.Remove(go);
		}
	}

	private void Update()
	{
		if (firstDeathGlow.Count > 0)
		{
			List<long> list = (from x in firstDeathGlow
				where Time.unscaledTime >= x.Value
				select x.Key).ToList();
			foreach (long item in list)
			{
				firstDeathGlow.Remove(item);
			}
			if (list.Count > 0)
			{
				RefreshBoards();
			}
		}
		if (!((Object)(object)Player.m_localPlayer == (Object)null) && !(Time.unscaledTime < noticeUntil))
		{
			currentNotice = ((notices.Count > 0) ? notices.Dequeue() : null);
			if (currentNotice != null)
			{
				noticeUntil = Time.unscaledTime + 7f;
			}
		}
	}

	private void OnGUI()
	{
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: 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_0080: Expected O, but got Unknown
		if (FeatureSwitches.DeathEnabled && !((Object)(object)Player.m_localPlayer == (Object)null) && Announcements.Value && !string.IsNullOrEmpty(currentNotice))
		{
			if (noticeStyle == null)
			{
				noticeStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)1,
					wordWrap = true
				};
			}
			noticeStyle.fontSize = Mathf.Clamp(Screen.height / 40, 18, 36);
			Rect val = default(Rect);
			((Rect)(ref val))..ctor((float)Screen.width * 0.1f, 8f, (float)Screen.width * 0.8f, 100f);
			noticeStyle.normal.textColor = Color.black;
			GUI.Label(new Rect(((Rect)(ref val)).x + 2f, ((Rect)(ref val)).y + 2f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), currentNotice, noticeStyle);
			noticeStyle.normal.textColor = new Color(1f, 0.8f, 0.2f);
			GUI.Label(val, currentNotice, noticeStyle);
		}
	}

	private static string CauseText(DeathAnnouncement data)
	{
		if (!string.IsNullOrEmpty(data.Killer))
		{
			string value = ((Localization.instance != null) ? Localization.instance.Localize(data.Killer) : data.Killer);
			return " was killed by " + Clean(value);
		}
		switch (data.Cause)
		{
		case "Fall":
			return " died from a fall";
		case "Drowning":
		case "Water":
			return " drowned";
		case "Burning":
		case "CinderFire":
			return " burned to death";
		case "Freezing":
			return " froze to death";
		case "Poisoned":
			return " died from poison";
		case "Smoke":
			return " died from smoke";
		case "Tree":
			return " was killed by a falling tree";
		case "AshlandsLava":
			return " died in lava";
		case "AshlandsOcean":
			return " died in boiling water";
		case "EdgeOfWorld":
			return " fell off the edge of the world";
		default:
			return " died";
		}
	}

	private void RequestState()
	{
		if (!((Object)(object)ZNet.instance == (Object)null) && ZRoutedRpc.instance != null)
		{
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(serverPeer.m_uid, "MoonforgedDeaths_Request", Array.Empty<object>());
			}
		}
	}

	private void SendRequestedState(long sender)
	{
		if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZNet.instance.GetPeer(sender) != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "MoonforgedDeaths_State", new object[1] { Serialize(totals) });
		}
	}

	internal void RegisterBoard(DeathScoreboard board)
	{
		if (!boards.Contains(board))
		{
			boards.Add(board);
		}
		RefreshBoards();
		RequestState();
	}

	internal void UnregisterBoard(DeathScoreboard board)
	{
		boards.Remove(board);
	}

	private void RefreshBoards()
	{
		List<DeathStoneRow> list = (PreviewBoard.Value ? DeathStoneLayout.Preview() : (from x in totals.Players
			orderby x.Deaths descending, x.PlayerName
			select new DeathStoneRow
			{
				Id = x.PlayerId,
				Name = Clean(x.PlayerName),
				Deaths = x.Deaths
			}).ToList());
		int num = Math.Max(1, (list.Count + 30 - 1) / 30);
		int start = page % num * 30;
		for (int num2 = boards.Count - 1; num2 >= 0; num2--)
		{
			if ((Object)(object)boards[num2] == (Object)null)
			{
				boards.RemoveAt(num2);
			}
			else if (!FeatureSwitches.DeathEnabled)
			{
				boards[num2].SetBoardText("Death Note disabled.");
			}
			else if (list.Count == 0)
			{
				boards[num2].SetBoardText("No deaths recorded yet.");
			}
			else
			{
				boards[num2].SetRows(list, start, (long id) => firstDeathGlow.TryGetValue(id, out var value) && Time.unscaledTime < value);
			}
		}
	}

	internal void RefreshFeatureState()
	{
		if (!FeatureSwitches.DeathEnabled)
		{
			notices.Clear();
			firstDeathGlow.Clear();
			currentNotice = null;
			foreach (GameObject activeDeathSound in activeDeathSounds)
			{
				if ((Object)(object)activeDeathSound != (Object)null)
				{
					Object.Destroy((Object)(object)activeDeathSound);
				}
			}
			activeDeathSounds.Clear();
		}
		RefreshBoards();
		if (FeatureSwitches.DeathEnabled)
		{
			RequestState();
		}
	}

	internal bool ClearTotalsForAdmin()
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return false;
		}
		DeathTotals value = new DeathTotals();
		string text = Serialize(value);
		try
		{
			FeatureSwitches.BackupBeforeReset(filePath);
			string text2 = filePath + ".tmp";
			File.WriteAllText(text2, text);
			if (File.Exists(filePath))
			{
				File.Replace(text2, filePath, filePath + ".bak");
			}
			else
			{
				File.Move(text2, filePath);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource modLogger = MoonforgedEventManager.ModLogger;
			if (modLogger != null)
			{
				modLogger.LogError((object)("Could not clear death totals: " + ex.Message));
			}
			return false;
		}
		totals = value;
		canSave = true;
		snapshot = text;
		ClearLocalEffects();
		ZRoutedRpc.instance.InvokeRoutedRPC(0L, "MoonforgedDeaths_State", new object[1] { text });
		return true;
	}

	internal void ClearLocalEffects()
	{
		PreviewBoard.Value = false;
		effectGeneration++;
		firstDeathGlow.Clear();
		notices.Clear();
		currentNotice = null;
		foreach (GameObject activeDeathSound in activeDeathSounds)
		{
			if ((Object)(object)activeDeathSound != (Object)null)
			{
				Object.Destroy((Object)(object)activeDeathSound);
			}
		}
		activeDeathSounds.Clear();
		page = 0;
		RefreshBoards();
	}

	private void LoadTotals()
	{
		filePath = Path.Combine(Paths.ConfigPath, "MoonforgedDeaths_" + ZNet.instance.GetWorldUID() + ".json");
		canSave = true;
		if (File.Exists(filePath))
		{
			try
			{
				totals = Deserialize<DeathTotals>(File.ReadAllText(filePath));
				if (totals?.Players == null)
				{
					throw new InvalidDataException("Missing death entries.");
				}
			}
			catch (Exception ex)
			{
				totals = new DeathTotals();
				canSave = false;
				ManualLogSource modLogger = MoonforgedEventManager.ModLogger;
				if (modLogger != null)
				{
					modLogger.LogError((object)("Could not read world death totals; preserving the saved file: " + ex.Message));
				}
			}
		}
		snapshot = Serialize(totals);
		RefreshBoards();
	}

	private void SaveTotals()
	{
		if (!canSave || string.IsNullOrEmpty(filePath))
		{
			return;
		}
		try
		{
			string text = filePath + ".tmp";
			File.WriteAllText(text, Serialize(totals));
			if (File.Exists(filePath))
			{
				File.Replace(text, filePath, filePath + ".bak");
			}
			else
			{
				File.Move(text, filePath);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource modLogger = MoonforgedEventManager.ModLogger;
			if (modLogger != null)
			{
				modLogger.LogError((object)("Could not save world death totals: " + ex.Message));
			}
		}
	}

	private static string Serialize<T>(T value)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		using MemoryStream memoryStream = new MemoryStream();
		((XmlObjectSerializer)new DataContractJsonSerializer(typeof(T))).WriteObject((Stream)memoryStream, (object)value);
		return Encoding.UTF8.GetString(memoryStream.ToArray());
	}

	private static T Deserialize<T>(string json)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		using MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json));
		return (T)((XmlObjectSerializer)new DataContractJsonSerializer(typeof(T))).ReadObject((Stream)memoryStream);
	}
}
[HarmonyPatch(typeof(Player), "OnDeath")]
internal static class PlayerDeathReportPatch
{
	private sealed class Capture
	{
		internal HitData Hit;
	}

	private static readonly FieldInfo LastHit = AccessTools.Field(typeof(Character), "m_lastHit");

	private static void Prefix(Player __instance, out Capture __state)
	{
		__state = (((Object)(object)__instance == (Object)(object)Player.m_localPlayer && !((Character)__instance).IsDead()) ? new Capture
		{
			Hit = (HitData)/*isinst with value type is only supported in some contexts*/
		} : null);
	}

	private static void Postfix(Player __instance, Capture __state)
	{
		if (__state != null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && ((Character)__instance).IsDead())
		{
			DeathTracker.Instance?.Report(__instance, __state.Hit);
		}
	}
}
[DataContract]
internal sealed class EventHistoryFile
{
	[DataMember]
	public List<CompletedEvent> Events = new List<CompletedEvent>();
}
[DataContract]
internal sealed class CompletedEvent
{
	[DataMember]
	public int Number;

	[DataMember]
	public string CompletedUtc;

	[DataMember]
	public int ParticipantCount;

	[DataMember]
	public List<CompletedEventResult> Results = new List<CompletedEventResult>();
}
[DataContract]
internal sealed class CompletedEventResult
{
	[DataMember]
	public string PlayerName;

	[DataMember]
	public float TimeSeconds;

	[DataMember]
	public int Deaths;

	[DataMember]
	public bool Finished;
}
internal static class EventHistoryArchive
{
	internal static EventHistoryFile Read(string path)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		if (!File.Exists(path))
		{
			return new EventHistoryFile();
		}
		using FileStream fileStream = File.OpenRead(path);
		EventHistoryFile eventHistoryFile = (EventHistoryFile)((XmlObjectSerializer)new DataContractJsonSerializer(typeof(EventHistoryFile))).ReadObject((Stream)fileStream);
		if (eventHistoryFile?.Events == null)
		{
			throw new InvalidDataException("Missing event history.");
		}
		HashSet<int> hashSet = new HashSet<int>();
		foreach (CompletedEvent @event in eventHistoryFile.Events)
		{
			if (@event == null || @event.Results == null || @event.Number <= 0 || !hashSet.Add(@event.Number))
			{
				throw new InvalidDataException("Invalid event history entry.");
			}
		}
		return eventHistoryFile;
	}

	internal static void Write(string path, EventHistoryFile data)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		string text = path + ".tmp";
		using (FileStream fileStream = File.Create(text))
		{
			((XmlObjectSerializer)new DataContractJsonSerializer(typeof(EventHistoryFile))).WriteObject((Stream)fileStream, (object)data);
		}
		if (File.Exists(path))
		{
			File.Replace(text, path, path + ".bak");
		}
		else
		{
			File.Move(text, path);
		}
	}

	internal static int BrowseIndex(int current, int direction, int count)
	{
		if (count <= 0)
		{
			return -1;
		}
		return Math.Max(0, Math.Min(count - 1, current + direction));
	}
}
internal static class FeatureSwitches
{
	private const string ClearRpc = "Moonforged_AdminClear";

	private const string ClearedRpc = "Moonforged_AdminCleared";

	private const string ResultRpc = "Moonforged_AdminClearResult";

	private static float confirmEventsUntil;

	private static float confirmDeathsUntil;

	private static ConfigEntry<bool> events;

	private static ConfigEntry<bool> deaths;

	private static readonly Dictionary<Piece, string> pieces = new Dictionary<Piece, string>();

	internal static bool EventEnabled => events == null || events.Value;

	internal static bool DeathEnabled => deaths == null || deaths.Value;

	internal static void Initialize(ConfigFile config)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Expected O, but got Unknown
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Expected O, but got Unknown
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Expected O, but got Unknown
		ConfigurationManagerAttributes val = new ConfigurationManagerAttributes();
		val.IsAdminOnly = true;
		val.CustomDrawer = DrawEvents;
		ConfigurationManagerAttributes val2 = val;
		val = new ConfigurationManagerAttributes();
		val.IsAdminOnly = true;
		val.CustomDrawer = DrawDeaths;
		ConfigurationManagerAttributes val3 = val;
		events = config.Bind<bool>("General", "EventManagerEnabled", true, new ConfigDescription("Enable Event Manager and its hammer pieces. Disabling cancels the current race, but preserves saved event history. Synced from the server.", (AcceptableValueBase)null, new object[1] { val2 }));
		deaths = config.Bind<bool>("General", "DeathNoteEnabled", true, new ConfigDescription("Enable world death totals, announcements, sounds and the Death Note hammer piece. Saved totals are preserved when disabled. Race-only death counts remain part of Event Manager. Synced from the server.", (AcceptableValueBase)null, new object[1] { val3 }));
		events.SettingChanged += delegate
		{
			Refresh();
		};
		deaths.SettingChanged += delegate
		{
			Refresh();
		};
	}

	private static void DrawEvents(ConfigEntryBase entry)
	{
		DrawFeature(events, "Delete Events", 0, ref confirmEventsUntil);
	}

	private static void DrawDeaths(ConfigEntryBase entry)
	{
		DrawFeature(deaths, "Clear Death Names", 1, ref confirmDeathsUntil);
	}

	private static void DrawFeature(ConfigEntry<bool> entry, string label, int kind, ref float until)
	{
		GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
		bool enabled = GUI.enabled;
		GUI.enabled = enabled && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.LocalPlayerIsAdminOrHost();
		bool flag = GUILayout.Toggle(entry.Value, entry.Value ? "On" : "Off", Array.Empty<GUILayoutOption>());
		if (flag != entry.Value)
		{
			entry.Value = flag;
		}
		bool flag2 = Time.unscaledTime < until;
		if (GUILayout.Button(flag2 ? ("Confirm " + label) : label, Array.Empty<GUILayoutOption>()))
		{
			if (!flag2)
			{
				until = Time.unscaledTime + 5f;
			}
			else
			{
				until = 0f;
				long num = (ZNet.instance.IsServer() ? ZNet.GetUID() : (ZNet.instance.GetServerPeer()?.m_uid ?? 0));
				if (num != 0L && ZRoutedRpc.instance != null)
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(num, "Moonforged_AdminClear", new object[1] { kind });
				}
			}
		}
		GUI.enabled = enabled;
		GUILayout.EndHorizontal();
	}

	internal static void RegisterAdminRpcs()
	{
		ZRoutedRpc.instance.Register<int>("Moonforged_AdminClear", (Action<long, int>)ServerClear);
		ZRoutedRpc.instance.Register<int>("Moonforged_AdminCleared", (Action<long, int>)ReceiveCleared);
		ZRoutedRpc.instance.Register<string>("Moonforged_AdminClearResult", (Action<long, string>)ReceiveResult);
	}

	private static bool IsServer(long sender)
	{
		int result;
		if ((Object)(object)ZNet.instance != (Object)null)
		{
			if (!ZNet.instance.IsServer())
			{
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				result = ((serverPeer != null && serverPeer.m_uid == sender) ? 1 : 0);
			}
			else
			{
				result = ((sender == ZNet.GetUID()) ? 1 : 0);
			}
		}
		else
		{
			result = 0;
		}
		return (byte)result != 0;
	}

	private static void ServerClear(long sender, int kind)
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (kind != 0 && kind != 1))
		{
			return;
		}
		if (sender != ZNet.GetUID())
		{
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			if (peer == null || peer.m_rpc == null || !ZNet.instance.IsAdmin(peer.m_rpc.GetSocket().GetHostName()))
			{
				return;
			}
		}
		bool flag = ((kind != 0) ? (DeathTracker.Instance?.ClearTotalsForAdmin() ?? false) : (MoonforgedTriathlonManager.Instance?.ClearEventsForAdmin() ?? false));
		if (flag)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "Moonforged_AdminCleared", new object[1] { kind });
		}
		ZRoutedRpc.instance.InvokeRoutedRPC(sender, "Moonforged_AdminClearResult", new object[1] { (!flag) ? "Could not clear saved data. Check the server log." : ((kind == 0) ? "Events and history cleared." : "Death names and totals cleared.") });
	}

	private static void ReceiveCleared(long sender, int kind)
	{
		if (IsServer(sender))
		{
			switch (kind)
			{
			case 0:
				MoonforgedTriathlonManager.PreviewEventBoard.Value = false;
				MoonforgedTriathlonManager.Instance?.RefreshClearedEvents();
				break;
			case 1:
				DeathTracker.Instance?.ClearLocalEffects();
				break;
			}
		}
	}

	private static void ReceiveResult(long sender, string message)
	{
		if (IsServer(sender) && (Object)(object)MessageHud.instance != (Object)null)
		{
			MessageHud.instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false, true);
		}
	}

	internal static void BackupBeforeReset(string path)
	{
		if (string.IsNullOrEmpty(path))
		{
			throw new InvalidOperationException("World data is not ready.");
		}
		if (File.Exists(path))
		{
			File.Copy(path, path + ".before-reset-" + DateTime.UtcNow.ToString("yyyyMMdd-HHmmss-fffffff") + ".bak", overwrite: false);
		}
	}

	internal static bool PieceEnabled(string name)
	{
		return (name == "M_EM_Death_Scoreboard") ? DeathEnabled : EventEnabled;
	}

	internal static void RegisterPiece(Piece piece, string name)
	{
		pieces[piece] = name;
		piece.m_enabled = PieceEnabled(name);
	}

	internal static void Refresh()
	{
		foreach (KeyValuePair<Piece, string> piece in pieces)
		{
			if ((Object)(object)piece.Key != (Object)null)
			{
				piece.Key.m_enabled = PieceEnabled(piece.Value);
			}
		}
		MoonforgedTriathlonManager.Instance?.RefreshFeatureState();
		DeathTracker.Instance?.RefreshFeatureState();
	}
}
public class FinishRuneInteract : MonoBehaviour, Hoverable, Interactable
{
	private Piece m_piece;

	private void Awake()
	{
		m_piece = ((Component)this).GetComponent<Piece>();
	}

	public bool Interact(Humanoid user, bool hold, bool alt)
	{
		if (hold)
		{
			return false;
		}
		Player val = (Player)(object)((user is Player) ? user : null);
		if ((Object)(object)val == (Object)null || (Object)(object)MoonforgedTriathlonManager.Instance == (Object)null)
		{
			return false;
		}
		if (MoonforgedTriathlonManager.Instance.IsRaceEnded())
		{
			MessageHud instance = MessageHud.instance;
			if (instance != null)
			{
				instance.ShowMessage((MessageType)2, "Event finished. Use Start Rune to reset.", 0, (Sprite)null, false, true);
			}
			return false;
		}
		MoonforgedTriathlonManager.Instance.RequestFinish(val);
		return true;
	}

	public bool UseItem(Humanoid user, ItemData item)
	{
		return false;
	}

	public string GetHoverText()
	{
		string text = (((Object)(object)m_piece != (Object)null) ? m_piece.m_name : "Finish Rune");
		if ((Object)(object)MoonforgedTriathlonManager.Instance != (Object)null && MoonforgedTriathlonManager.Instance.IsRaceEnded())
		{
			return text + "\nEvent finished";
		}
		return text + "\n[<color=yellow><b>E</b></color>] Register my finish";
	}

	public float GetHoverOffset()
	{
		return 0f;
	}

	public string GetHoverName()
	{
		return ((Object)(object)m_piece != (Object)null) ? m_piece.m_name : "Finish Rune";
	}
}
public sealed class GateNightLights : MonoBehaviour
{
	private sealed class Bulb
	{
		public Renderer Renderer;

		public int MaterialIndex;

		public Color Emission;
	}

	private const string StateKey = "MoonforgedGateNight";

	private readonly List<GameObject> lightObjects = new List<GameObject>();

	private readonly List<Bulb> bulbs = new List<Bulb>();

	private ZNetView view;

	private float nextCheck;

	private bool initialized;

	private bool lastState;

	internal static void PreparePrefab(GameObject prefab)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Invalid comparison between Unknown and I4
		Light[] componentsInChildren = prefab.GetComponentsInChildren<Light>(true);
		foreach (Light val in componentsInChildren)
		{
			if ((int)val.type == 2)
			{
				if (val.intensity <= 0f)
				{
					val.intensity = 3f;
				}
				((Behaviour)val).enabled = true;
			}
		}
		Renderer[] componentsInChildren2 = prefab.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val2 in componentsInChildren2)
		{
			if (!((Object)val2).name.Equals("bolb", StringComparison.OrdinalIgnoreCase) && !((Object)val2).name.Equals("bulb", StringComparison.OrdinalIgnoreCase))
			{
				continue;
			}
			Material[] sharedMaterials = val2.sharedMaterials;
			foreach (Material val3 in sharedMaterials)
			{
				if ((Object)(object)val3 != (Object)null && val3.HasProperty("_EmissionColor"))
				{
					val3.EnableKeyword("_EMISSION");
				}
			}
		}
	}

	private void Start()
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Invalid comparison between Unknown and I4
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		view = ((Component)this).GetComponent<ZNetView>();
		Light[] componentsInChildren = ((Component)this).GetComponentsInChildren<Light>(true);
		foreach (Light val in componentsInChildren)
		{
			if ((int)val.type == 2 && !lightObjects.Contains(((Component)val).gameObject))
			{
				lightObjects.Add(((Component)val).gameObject);
			}
		}
		Transform[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<Transform>(true);
		foreach (Transform val2 in componentsInChildren2)
		{
			if (((Object)val2).name.Equals("flare", StringComparison.OrdinalIgnoreCase) && !lightObjects.Contains(((Component)val2).gameObject))
			{
				lightObjects.Add(((Component)val2).gameObject);
			}
		}
		Renderer[] componentsInChildren3 = ((Component)this).GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val3 in componentsInChildren3)
		{
			if (!((Object)val3).name.Equals("bolb", StringComparison.OrdinalIgnoreCase) && !((Object)val3).name.Equals("bulb", StringComparison.OrdinalIgnoreCase))
			{
				continue;
			}
			Material[] sharedMaterials = val3.sharedMaterials;
			for (int l = 0; l < sharedMaterials.Length; l++)
			{
				if (Object.op_Implicit((Object)(object)sharedMaterials[l]) && sharedMaterials[l].HasProperty("_EmissionColor"))
				{
					bulbs.Add(new Bulb
					{
						Renderer = val3,
						MaterialIndex = l,
						Emission = sharedMaterials[l].GetColor("_EmissionColor")
					});
				}
			}
		}
		if (Player.IsPlacementGhost(((Component)this).gameObject))
		{
			Apply(state: false);
			((Behaviour)this).enabled = false;
		}
		else
		{
			Update();
		}
	}

	private void Update()
	{
		if (Object.op_Implicit((Object)(object)view) && view.IsValid() && Object.op_Implicit((Object)(object)EnvMan.instance) && !(Time.time < nextCheck))
		{
			nextCheck = Time.time + 1f;
			ZDO zDO = view.GetZDO();
			bool flag = EnvMan.IsNight();
			if (view.IsOwner() && zDO.GetBool("MoonforgedGateNight", false) != flag)
			{
				zDO.Set("MoonforgedGateNight", flag);
			}
			bool flag2 = zDO.GetBool("MoonforgedGateNight", false);
			if (!initialized || flag2 != lastState)
			{
				Apply(flag2);
			}
		}
	}

	private void Apply(bool state)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		initialized = true;
		lastState = state;
		foreach (GameObject lightObject in lightObjects)
		{
			if (Object.op_Implicit((Object)(object)lightObject))
			{
				lightObject.SetActive(state);
			}
		}
		MaterialPropertyBlock val = new MaterialPropertyBlock();
		foreach (Bulb bulb in bulbs)
		{
			if (Object.op_Implicit((Object)(object)bulb.Renderer))
			{
				val.Clear();
				bulb.Renderer.GetPropertyBlock(val, bulb.MaterialIndex);
				val.SetColor("_EmissionColor", state ? bulb.Emission : Color.black);
				bulb.Renderer.SetPropertyBlock(val, bulb.MaterialIndex);
			}
		}
	}
}
public class HornInteract : MonoBehaviour, Hoverable, Interactable
{
	private Piece m_piece;

	private void Awake()
	{
		m_piece = ((Component)this).GetComponent<Piece>();
	}

	private void Start()
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Expected O, but got Unknown
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected O, but got Unknown
		Switch[] componentsInChildren = ((Component)this).GetComponentsInChildren<Switch>(true);
		foreach (Switch val in componentsInChildren)
		{
			val.m_onUse = (Callback)((Switch source, Humanoid user, ItemData item) => item == null && Interact(user, hold: false, alt: false));
			val.m_onHover = new TooltipCallback(GetHoverText);
			val.m_name = GetHoverName();
		}
	}

	public bool Interact(Humanoid user, bool hold, bool alt)
	{
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		if (hold)
		{
			return false;
		}
		Player val = (Player)(object)((user is Player) ? user : null);
		if ((Object)(object)val == (Object)null || (Object)(object)MoonforgedTriathlonManager.Instance == (Object)null)
		{
			return false;
		}
		if (!MoonforgedTriathlonManager.Instance.IsConfiguredAdmin(val))
		{
			if ((Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.ShowMessage((MessageType)2, "Only configured admin can start the race.", 0, (Sprite)null, false, true);
			}
			return false;
		}
		MoonforgedTriathlonManager.Instance.RequestStartRace(val, ((Component)this).transform.position);
		return true;
	}

	public bool UseItem(Humanoid user, ItemData item)
	{
		return false;
	}

	public string GetHoverText()
	{
		string text = (((Object)(object)m_piece != (Object)null) ? m_piece.m_name : "Horn");
		return text + "\n[<color=yellow><b>E</b></color>] Start Moonforged Race";
	}

	public float GetHoverOffset()
	{
		return 0f;
	}

	public string GetHoverName()
	{
		return ((Object)(object)m_piece != (Object)null) ? m_piece.m_name : "Horn";
	}
}
internal static class HornSoundSettings
{
	private static readonly Dictionary<string, AudioClip> clips = new Dictionary<string, AudioClip>();

	internal static ConfigEntry<string> SelectedSound;

	internal static ConfigEntry<string> SelectedEndSound;

	private static ConfigEntry<float> HornVolumePercent;

	private static AudioSource preview;

	private static AudioSource template;

	private static AudioMixer hornMixer;

	private static readonly Dictionary<AudioSource, float> activeSources = new Dictionary<AudioSource, float>();

	internal static void Initialize(ConfigFile config)
	{
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Expected O, but got Unknown
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Expected O, but got Unknown
		//IL_00ae: 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_00bc: Expected O, but got Unknown
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Expected O, but got Unknown
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Expected O, but got Unknown
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Expected O, but got Unknown
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Expected O, but got Unknown
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Expected O, but got Unknown
		SelectedSound = config.Bind<string>("Race Horn", "HornSound", "Viking War Horn 1", new ConfigDescription("Sound played before the race countdown. Synced from the server.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[4] { "Fart Echo", "Viking War Horn 1", "Viking War Horn 2", "Viking War Horn 3" }), new object[1] { (object)new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		} }));
		SelectedEndSound = config.Bind<string>("Race Horn", "EndSound", "Viking War Horn 3", new ConfigDescription("Sound played automatically when the final registered racer finishes. Synced from the server.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[4] { "Fart Echo", "Viking War Horn 1", "Viking War Horn 2", "Viking War Horn 3" }), new object[1] { (object)new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		} }));
		HornVolumePercent = config.Bind<float>("Race Horn", "HornVolumePercent", 100f, new ConfigDescription("Horn volume percentage for race playback and local previews. 0 = muted, 100 = original, 200 = double gain. High values may distort. Synced from the server.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), new object[1] { (object)new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		} }));
		HornVolumePercent.SettingChanged += delegate
		{
			ApplyVolume();
		};
		object[] array = new object[1];
		ConfigurationManagerAttributes val = new ConfigurationManagerAttributes();
		val.CustomDrawer = DrawPreview;
		array[0] = val;
		config.Bind<bool>("Race Horn", "PreviewSound", false, new ConfigDescription("Preview the selected sound locally without starting a race.", (AcceptableValueBase)null, array));
	}

	internal static void Load(AssetBundle bundle)
	{
		clips["Fart Echo"] = bundle.LoadAsset<AudioClip>("valheim_echo_loud_fart");
		clips["Viking War Horn 1"] = bundle.LoadAsset<AudioClip>("assets/imperium events/horn/materials/viking-war-horn_1.ogg");
		clips["Viking War Horn 2"] = bundle.LoadAsset<AudioClip>("assets/imperium events/horn/materials/viking-war-horn_2.ogg");
		clips["Viking War Horn 3"] = bundle.LoadAsset<AudioClip>("assets/imperium events/horn/materials/viking-war-horn_3.ogg");
		GameObject val = bundle.LoadAsset<GameObject>("M_EM_Horn");
		if ((Object)(object)val != (Object)null)
		{
			template = val.GetComponent<AudioSource>();
			if ((Object)(object)template != (Object)null && (Object)(object)template.outputAudioMixerGroup != (Object)null)
			{
				hornMixer = template.outputAudioMixerGroup.audioMixer;
			}
			ZSFX component = val.GetComponent<ZSFX>();
			if ((Object)(object)component != (Object)null && component.m_audioClips != null && component.m_audioClips.Length != 0)
			{
				clips["Fart Echo"] = component.m_audioClips[0];
			}
		}
	}

	internal static float Duration(string selection)
	{
		AudioClip value;
		return (clips.TryGetValue(selection, out value) && (Object)(object)value != (Object)null) ? value.length : 0f;
	}

	internal static void Play(string selection, Vector3 position, bool localPreview = false, bool global = false)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected O, but got Unknown
		//IL_003e: 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)
		if (clips.TryGetValue(selection, out var value) && !((Object)(object)value == (Object)null))
		{
			GameObject val = new GameObject(localPreview ? "Moonforged horn preview" : "Moonforged race horn sound");
			val.transform.position = position;
			AudioSource val2 = val.AddComponent<AudioSource>();
			val2.playOnAwake = false;
			val2.loop = false;
			val2.pitch = 1f;
			val2.spatialBlend = ((localPreview || global) ? 0f : 1f);
			if ((Object)(object)template != (Object)null)
			{
				val2.outputAudioMixerGroup = template.outputAudioMixerGroup;
				val2.volume = template.volume;
				val2.minDistance = template.minDistance;
				val2.maxDistance = template.maxDistance;
				val2.rolloffMode = template.rolloffMode;
			}
			activeSources[val2] = val2.volume;
			ApplyVolume();
			val2.PlayOneShot(value, 1f);
			if (localPreview)
			{
				preview = val2;
			}
			Object.Destroy((Object)(object)val, value.length + 0.25f);
		}
	}

	private static void ApplyVolume()
	{
		float num = Mathf.Clamp(HornVolumePercent.Value, 0f, 300f) / 100f;
		float num2 = ((num > 0f) ? Mathf.Clamp(20f * Mathf.Log10(num), -80f, 20f) : (-80f));
		bool flag = (Object)(object)hornMixer != (Object)null && hornMixer.SetFloat("HornVolume", num2);
		List<AudioSource> list = new List<AudioSource>();
		foreach (KeyValuePair<AudioSource, float> activeSource in activeSources)
		{
			if ((Object)(object)activeSource.Key == (Object)null)
			{
				list.Add(activeSource.Key);
			}
			else
			{
				activeSource.Key.volume = activeSource.Value * ((num <= 0f) ? 0f : (flag ? 1f : Mathf.Clamp01(num)));
			}
		}
		foreach (AudioSource item in list)
		{
			activeSources.Remove(item);
		}
	}

	private static void DrawPreview(ConfigEntryBase entry)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
		if (GUILayout.Button("Play Preview", Array.Empty<GUILayoutOption>()))
		{
			StopPreview();
			Play(SelectedSound.Value, Vector3.zero, localPreview: true);
		}
		if (GUILayout.Button("Preview End Sound", Array.Empty<GUILayoutOption>()))
		{
			StopPreview();
			Play(SelectedEndSound.Value, Vector3.zero, localPreview: true);
		}
		if (GUILayout.Button("Stop Preview", Array.Empty<GUILayoutOption>()))
		{
			StopPreview();
		}
		GUILayout.EndHorizontal();
	}

	internal static void StopRaceSounds()
	{
		List<AudioSource> list = new List<AudioSource>();
		foreach (KeyValuePair<AudioSource, float> activeSource in activeSources)
		{
			if ((Object)(object)activeSource.Key == (Object)null || (Object)(object)activeSource.Key != (Object)(object)preview)
			{
				if ((Object)(object)activeSource.Key != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)activeSource.Key).gameObject);
				}
				list.Add(activeSource.Key);
			}
		}
		foreach (AudioSource item in list)
		{
			activeSources.Remove(item);
		}
	}

	private static void StopPreview()
	{
		if ((Object)(object)preview != (Object)null)
		{
			activeSources.Remove(preview);
			Object.Destroy((Object)(object)((Component)preview).gameObject);
		}
		preview = null;
	}
}
[BepInPlugin("Caenos.MoonforgedEventManager", "Moonforged Event Manager", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class MoonforgedEventManager : BaseUnityPlugin
{
	public const string PluginGUID = "Caenos.MoonforgedEventManager";

	public const string PluginName = "Moonforged Event Manager";

	public const string PluginVersion = "1.0.0";

	private AssetBundle relicsBundle;

	public static ConfigEntry<string> PlayerPreferredCategory;

	public static ManualLogSource ModLogger;

	internal static ConfigEntry<bool> ShowServerName;

	internal static ConfigEntry<string> ServerNameText;

	internal static ConfigEntry<float> ServerNameFontSize;

	internal static ConfigEntry<string> ServerNameColor;

	internal static ConfigEntry<float> ServerNamePosX;

	internal static ConfigEntry<float> ServerNamePosY;

	internal static ConfigEntry<float> ServerNamePosZ;

	private void Awake()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: 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_004e: Expected O, but got Unknown
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Expected O, but got Unknown
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Expected O, but got Unknown
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Expected O, but got Unknown
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Expected O, but got Unknown
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_0163: Expected O, but got Unknown
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Expected O, but got Unknown
		//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cb: Expected O, but got Unknown
		ModLogger = ((BaseUnityPlugin)this).Logger;
		new Harmony("caenos.moonforgedeventmanager.harmony").PatchAll();
		PlayerPreferredCategory = ((BaseUnityPlugin)this).Config.Bind<string>("General", "CustomHammerTab", "Moonforged Event Manager", "Custom hammer tab category name");
		ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		};
		FeatureSwitches.Initialize(((BaseUnityPlugin)this).Config);
		ShowServerName = ((BaseUnityPlugin)this).Config.Bind<bool>("Banner Text", "ShowServerName", true, new ConfigDescription("Display the server name on the hanging banner.", (AcceptableValueBase)null, new object[1] { val }));
		ServerNameText = ((BaseUnityPlugin)this).Config.Bind<string>("Banner Text", "ServerNameText", "MY SERVER NAME", new ConfigDescription("Server name displayed on the hanging banner. Synced to all players.", (AcceptableValueBase)null, new object[1] { val }));
		ServerNameFontSize = ((BaseUnityPlugin)this).Config.Bind<float>("Banner Text", "ServerNameFontSize", 23f, new ConfigDescription("TextMeshPro font size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), new object[1] { val }));
		ServerNameColor = ((BaseUnityPlugin)this).Config.Bind<string>("Banner Text", "ServerNameColor", "#E5C16A", new ConfigDescription("Text color in hexadecimal format (example: #E5C16A).", (AcceptableValueBase)null, new object[1] { val }));
		ServerNamePosX = ((BaseUnityPlugin)this).Config.Bind<float>("Banner Text", "ServerNamePosX", 0f, new ConfigDescription("Text local horizontal position on Banner_Cloth.", (AcceptableValueBase)null, new object[1] { val }));
		ServerNamePosY = ((BaseUnityPlugin)this).Config.Bind<float>("Banner Text", "ServerNamePosY", -0.873f, new ConfigDescription("Text local vertical position on Banner_Cloth.", (AcceptableValueBase)null, new object[1] { val }));
		ServerNamePosZ = ((BaseUnityPlugin)this).Config.Bind<float>("Banner Text", "ServerNamePosZ", 0.025f, new ConfigDescription("Text local depth position on Banner_Cloth.", (AcceptableValueBase)null, new object[1] { val }));
		ShowServerName.SettingChanged += OnBannerConfigChanged;
		ServerNameText.SettingChanged += OnBannerConfigChanged;
		ServerNameFontSize.SettingChanged += OnBannerConfigChanged;
		ServerNameColor.SettingChanged += OnBannerConfigChanged;
		ServerNamePosX.SettingChanged += OnBannerConfigChanged;
		ServerNamePosY.SettingChanged += OnBannerConfigChanged;
		ServerNamePosZ.SettingChanged += OnBannerConfigChanged;
		SynchronizationManager.OnConfigurationSynchronized += delegate
		{
			BannerServerText.RefreshAll();
		};
		SynchronizationManager.OnConfigurationSynchronized += delegate
		{
			FeatureSwitches.Refresh();
		};
		DeathTracker.Initialize(((BaseUnityPlugin)this).Config);
		((Component)this).gameObject.AddComponent<DeathTracker>();
		HornSoundSettings.Initialize(((BaseUnityPlugin)this).Config);
		MoonforgedTriathlonManager.Initialize(((BaseUnityPlugin)this).Config);
		((Component)this).gameObject.AddComponent<MoonforgedTriathlonManager>();
		Assembly executingAssembly = Assembly.GetExecutingAssembly();
		string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string r) => r.ToLower().Contains("moonforgedeventmanager"));
		if (string.IsNullOrEmpty(text))
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"Could not find embedded asset bundle resource containing 'moonforgedeventmanager'.");
			return;
		}
		using (Stream stream = executingAssembly.GetManifestResourceStream(text))
		{
			if (stream == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Embedded asset bundle stream was null for '" + text + "'."));
				return;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			relicsBundle = AssetBundle.LoadFromMemory(memoryStream.ToArray());
		}
		if ((Object)(object)relicsBundle == (Object)null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"Failed to load AssetBundle from embedded resource!");
			return;
		}
		HornSoundSettings.Load(relicsBundle);
		DeathTracker.Load(relicsBundle);
		PrefabManager.OnPrefabsRegistered -= RegisterNow;
		PrefabManager.OnPrefabsRegistered += RegisterNow;
	}

	private void OnBannerConfigChanged(object sender, EventArgs args)
	{
		BannerServerText.RefreshAll();
	}

	private void RegisterNow()
	{
		if (!((Object)(object)relicsBundle == (Object)null))
		{
			RelicRegistrar.RegisterAllRelics(relicsBundle);
			MoonforgedTriathlonBootstrap.AttachTriathlonComponents(((BaseUnityPlugin)this).Logger);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[Moonforged Event Manager] Registration complete. Hammer tab: '" + PlayerPreferredCategory.Value + "'."));
		}
	}
}
public static class MoonforgedTriathlonBootstrap
{
	public static void AttachTriathlonComponents(ManualLogSource logger)
	{
		AttachIfFound("M_EM_Horn", typeof(HornInteract), logger);
		AttachIfFound("M_EM_Start_Rune", typeof(StartRuneInteract), logger);
		AttachIfFound("M_EM_Finish_Rune", typeof(FinishRuneInteract), logger);
		AttachIfFound("M_EM_Scoreboard", typeof(RaceScoreboard), logger);
		AttachIfFound("M_EM_Death_Scoreboard", typeof(DeathScoreboard), logger);
		AttachNightLight("M_EM_Death_Scoreboard", logger);
	}

	private static void AttachIfFound(string prefabName, Type componentType, ManualLogSource logger)
	{
		GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName);
		if ((Object)(object)prefab == (Object)null)
		{
			if (logger != null)
			{
				logger.LogWarning((object)("[MoonforgedEventManager] Prefab not found for triathlon hookup: " + prefabName));
			}
		}
		else if ((Object)(object)prefab.GetComponent(componentType) == (Object)null)
		{
			prefab.AddComponent(componentType);
		}
	}

	private static void AttachNightLight(string prefabName, ManualLogSource logger)
	{
		GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName);
		if ((Object)(object)prefab == (Object)null)
		{
			if (logger != null)
			{
				logger.LogWarning((object)("[MoonforgedEventManager] " + prefabName + " prefab not found."));
			}
			return;
		}
		Transform val = prefab.transform.Find("NightLight");
		if (!((Object)(object)val == (Object)null) && (Object)(object)((Component)val).GetComponent<ScoreboardEmissionNight>() == (Object)null)
		{
			((Component)val).gameObject.AddComponent<ScoreboardEmissionNight>();
		}
	}
}
public class MoonforgedTriathlonManager : MonoBehaviour
{
	[Serializable]
	private struct EntrantState
	{
		public string PlayerName;

		public int Deaths;

		public bool Registered;

		public bool Finished;

		public double StartTime;

		public double FinishTime;
	}

	[Serializable]
	private class RaceResultsFile
	{
		public string EventName;

		public string SavedAt;

		public List<RaceResultEntry> Results = new List<RaceResultEntry>();
	}

	[Serializable]
	private class RaceResultEntry
	{
		public string PlayerName;

		public int Deaths;

		public float TimeSeconds;

		public string TimeFormatted;
	}

	private static class MiniJson
	{
		public static string Serialize(RaceResultsFile file)
		{
			if (file == null)
			{
				return "{}";
			}
			string text = "{\n";
			text = text + "  \"EventName\": " + Quote(file.EventName) + ",\n";
			text = text + "  \"SavedAt\": " + Quote(file.SavedAt) + ",\n";
			text += "  \"Results\": [\n";
			for (int i = 0; i < file.Results.Count; i++)
			{
				RaceResultEntry raceResultEntry = file.Results[i];
				text += "    {\n";
				text = text + "      \"PlayerName\": " + Quote(raceResultEntry.PlayerName) + ",\n";
				text = text + "      \"Deaths\": " + raceResultEntry.Deaths + ",\n";
				text = text + "      \"TimeSeconds\": " + raceResultEntry.TimeSeconds.ToString(CultureInfo.InvariantCulture) + ",\n";
				text = text + "      \"TimeFormatted\": " + Quote(raceResultEntry.TimeFormatted) + "\n";
				text += "    }";
				if (i < file.Results.Count - 1)
				{
					text += ",";
				}
				text += "\n";
			}
			text += "  ]\n";
			return text + "}";
		}

		private static string Quote(string value)
		{
			if (value == null)
			{
				return "\"\"";
			}
			return "\"" + value.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n")
				.Replace("\r", "\\r")
				.Replace("\t", "\\t") + "\"";
		}
	}

	public static MoonforgedTriathlonManager Instance;

	private const string RpcRegister = "MoonforgedTriathlon_Register";

	private const string RpcFinish = "MoonforgedTriathlon_Finish";

	private const string RpcStartRace = "MoonforgedTriathlon_StartRace";

	private const string RpcAdminReset = "MoonforgedTriathlon_AdminReset";

	private const string RpcBoardText = "MoonforgedTriathlon_BoardText";

	private const string RpcCenterMessage = "MoonforgedTriathlon_CenterMessage";

	private const string RpcResultsPopup = "MoonforgedTriathlon_ResultsPopup";

	private EventHistoryFile eventHistory = new EventHistoryFile();

	private string historyPath;

	private bool historyWritable;

	private int selectedHistoryIndex = -1;

	private int pendingHistoryIndex = -1;

	private string lastHistoryColumn = "EVENT HISTORY\n\nNo completed events yet.";

	private string lastHistorySelection = "No saved events";

	private const string RpcHistorySelect = "MoonforgedTriathlon_HistorySelect";

	private const string RpcHistoryDisplay = "MoonforgedTriathlon_HistoryDisplay";

	private string raceStartSound;

	private Vector3 raceHornPosition;

	private const string RpcFinishHorn = "MoonforgedTriathlon_FinishHorn";

	private const string RpcCancelRace = "MoonforgedTriathlon_CancelRace";

	private const string RpcStopHorn = "MoonforgedTriathlon_StopHorn";

	private Coroutine startSequence;

	private int deathPage;

	private float nextDeathPage;

	private const string RpcHornSound = "MoonforgedTriathlon_HornSound";

	private const string RpcState = "MoonforgedTriathlon_State";

	private const string RpcRequestState = "MoonforgedTriathlon_RequestState";

	internal static ConfigEntry<bool> PreviewEventBoard;

	private static ConfigEntry<string> AdminPlayerNames;

	private static ConfigEntry<string> ResultsFileName;

	private static ConfigEntry<float> CountdownSeconds;

	private static ConfigEntry<bool> RequireConfiguredAdminForStartAndReset;

	private readonly Dictionary<string, EntrantState> entrants = new Dictionary<string, EntrantState>(StringComparer.OrdinalIgnoreCase);

	private readonly List<RaceScoreboard> localBoards = new List<RaceScoreboard>();

	private bool registrationOpen = true;

	private bool raceStarted = false;

	private bool raceEnded = false;

	private double raceStartServerTime = 0.0;

	private bool rpcsRegistered = false;

	private ZRoutedRpc registeredRpc;

	private string lastBoardText;

	public static void Initialize(ConfigFile config)
	{
		//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: Expected O, but got Unknown
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Expected O, but got Unknown
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Expected O, but got Unknown
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Expected O, but got Unknown
		//IL_00d9: 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_00e7: Expected O, but got Unknown
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Expected O, but got Unknown
		PreviewEventBoard = config.Bind<bool>("Moonforged Event Manager", "PreviewEventBoard", false, "Local layout preview with ten sample racers. Does not register players or change saved race results. Turn off to show the real race.");
		AdminPlayerNames = config.Bind<string>("Moonforged Event Manager", "AdminPlayerNames", "Caenos", new ConfigDescription("Comma separated character names allowed to start/reset the event. Example: Caenos,Caen", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		} }));
		ResultsFileName = config.Bind<string>("Moonforged Event Manager", "ResultsFileName", "MoonforgedTriathlonResults.json", "JSON file written into BepInEx/config.");
		CountdownSeconds = config.Bind<float>("Moonforged Event Manager", "CountdownSeconds", 5f, new ConfigDescription("Countdown after the horn sound finishes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), new object[1] { (object)new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		} }));
		RequireConfiguredAdminForStartAndReset = config.Bind<bool>("Moonforged Event Manager", "RequireConfiguredAdminForStartAndReset", true, new ConfigDescription("If true, only names in AdminPlayerNames can start/reset the race.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
		{
			IsAdminOnly = true
		} }));
	}

	private void Awake()
	{
		if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
		{
			Object.Destroy((Object)(object)this);
			return;
		}
		Instance = this;
		Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
		((MonoBehaviour)this).StartCoroutine(RegisterRpcsWhenReady());
	}

	private IEnumerator RegisterRpcsWhenReady()
	{
		while (true)
		{
			if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null)
			{
				yield return null;
				continue;
			}
			if (registeredRpc != ZRoutedRpc.instance)
			{
				if (startSequence != null)
				{
					((MonoBehaviour)this).StopCoroutine(startSequence);
				}
				startSequence = null;
				ResetRaceServer();
				MoonforgedTriathlonManager moonforgedTriathlonManager = this;
				MoonforgedTriathlonManager moonforgedTriathlonManager2 = this;
				int num = -1;
				moonforgedTriathlonManager2.pendingHistoryIndex = -1;
				moonforgedTriathlonManager.selectedHistoryIndex = num;
				lastHistoryColumn = "EVENT HISTORY\n\nLoading history...";
				lastHistorySelection = "Loading history...";
				if (ZNet.instance.IsServer())
				{
					LoadEventHistory();
				}
				lastBoardText = null;
				FeatureSwitches.RegisterAdminRpcs();
				ZRoutedRpc.instance.Register<string, int>("MoonforgedTriathlon_HistorySelect", (Action<long, string, int>)RPC_HistorySelect);
				ZRoutedRpc.instance.Register<string, string>("MoonforgedTriathlon_HistoryDisplay", (Action<long, string, string>)RPC_HistoryDisplay);
				ZRoutedRpc.instance.Register<string, Vector3>("MoonforgedTriathlon_FinishHorn", (Action<long, string, Vector3>)RPC_FinishHorn);
				ZRoutedRpc.instance.Register<string>("MoonforgedTriathlon_CancelRace", (Action<long, string>)RPC_CancelRace);
				ZRoutedRpc.instance.Register("MoonforgedTriathlon_StopHorn", (Action<long>)RPC_StopHorn);
				ZRoutedRpc.instance.Register<string>("MoonforgedTriathlon_Register", (Action<long, string>)RPC_Register);
				ZRoutedRpc.instance.Register<string>("MoonforgedTriathlon_Finish", (Action<long, string>)RPC_Finish);
				ZRoutedRpc.instance.Register<string, Vector3>("MoonforgedTriathlon_StartRace", (Action<long, string, Vector3>)RPC_StartRace);
				ZRoutedRpc.instance.Register<string, Vector3>("MoonforgedTriathlon_HornSound", (Action<long, string, Vector3>)RPC_HornSound);
				ZRoutedRpc.instance.Register<string>("MoonforgedTriathlon_AdminReset", (Action<long, string>)RPC_AdminReset);
				ZRoutedRpc.instance.Register<string>("MoonforgedTriathlon_BoardText", (Action<long, string>)RPC_BoardText);
				ZRoutedRpc.instance.Register<string>("MoonforgedTriathlon_CenterMessage", (Action<long, string>)RPC_CenterMessage);
				ZRoutedRpc.instance.Register<string>("MoonforgedTriathlon_ResultsPopup", (Action<long, string>)RPC_ResultsPopup);
				ZRoutedRpc.instance.Register<string>("MoonforgedTriathlon_State", (Action<long, string>)RPC_State);
				ZRoutedRpc.instance.Register("MoonforgedTriathlon_RequestState", (Action<long>)RPC_RequestState);
				registeredRpc = ZRoutedRpc.instance;
				rpcsRegistered = true;
				RequestState();
			}
			else if (FeatureSwitches.EventEnabled && !ZNet.instance.IsServer() && lastBoardText == null)
			{
				RequestState();
			}
			if (FeatureSwitches.EventEnabled && ZNet.instance.IsServer() && Time.unscaledTime >= nextDeathPage)
			{
				nextDeathPage = Time.unscaledTime + 10f;
				deathPage++;
				BroadcastBoardText(BuildBoardText());
			}
			yield return (object)new WaitForSecondsRealtime(1f);
		}
	}

	public void ForceEndRace(