Decompiled source of DeathRoulette v1.6.0

DeathRoulette.dll

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

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DeathRoulette")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.6.0.0")]
[assembly: AssemblyInformationalVersion("1.6.0+231f763326f6e137bd0900b7be91d12dad6436fc")]
[assembly: AssemblyProduct("DeathRoulette")]
[assembly: AssemblyTitle("DeathRoulette")]
[assembly: AssemblyVersion("1.6.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DeathRoulette
{
	internal static class Announce
	{
		public static void Death(DeathContext ctx)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.AnnounceDeaths.Value)
			{
				string text = DeathCause.Describe(ctx.CharacterId);
				ServerActions.ShowMessage((MessageType)1, (text == null) ? (ctx.PlayerName + " has died") : (ctx.PlayerName + " has died — " + text));
			}
		}

		public static void Effect(DeathContext ctx, RouletteEvent ev, string flavour)
		{
			if (Plugin.AnnounceEffects.Value)
			{
				ServerActions.ShowMessage((MessageType)2, ev.DisplayName);
				ServerActions.ShowMessage((MessageType)1, flavour);
			}
		}

		public static void Manual(string byPlayer, RouletteEvent ev, string flavour, Vector3 at)
		{
			if (Plugin.AnnounceEffects.Value)
			{
				ServerActions.ShowMessage((MessageType)2, ev.DisplayName);
				ServerActions.ShowMessage((MessageType)1, flavour + "  (test fired by " + byPlayer + ")");
			}
		}
	}
	internal static class CommandFile
	{
		private const float PollSeconds = 1f;

		private static float _nextPoll;

		private static string _path;

		private static string _outPath;

		public static void Init(string configDir)
		{
			_path = Path.Combine(configDir, "deathroulette.cmd");
			_outPath = Path.Combine(configDir, "deathroulette.out");
			Plugin.Log.LogInfo((object)("Command file: " + _path));
		}

		public static void Poll()
		{
			if (_path == null || Time.realtimeSinceStartup < _nextPoll)
			{
				return;
			}
			_nextPoll = Time.realtimeSinceStartup + 1f;
			if (!File.Exists(_path))
			{
				return;
			}
			string[] array;
			try
			{
				array = File.ReadAllLines(_path);
				File.Delete(_path);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not read the command file: " + ex.Message));
				return;
			}
			List<string> list = new List<string>();
			string[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				string text = array2[i].Trim();
				if (text.Length != 0 && !text.StartsWith("#"))
				{
					try
					{
						list.AddRange(Execute(text));
					}
					catch (Exception ex2)
					{
						list.Add("'" + text + "' threw: " + ex2.Message);
						Plugin.Log.LogError((object)$"Command '{text}' threw: {ex2}");
					}
				}
			}
			foreach (string item in list)
			{
				Plugin.Log.LogInfo((object)("[cmd] " + item));
			}
			try
			{
				File.WriteAllLines(_outPath, list);
			}
			catch (Exception ex3)
			{
				Plugin.Log.LogWarning((object)("Could not write command output: " + ex3.Message));
			}
		}

		internal static IEnumerable<string> Execute(string line)
		{
			string[] array = line.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			string text = array[0].ToLowerInvariant();
			string text2 = ((array.Length > 1) ? array[1] : null);
			switch (text)
			{
			case "help":
				yield return "commands: list | status | possible | fire <id> | roll | fx <prefab> | stopraid | reload";
				break;
			case "list":
				foreach (RouletteEvent item in RouletteEvents.Table)
				{
					yield return $"{item.Id,-18} w={item.Weight,-3} [{item.Flavour}] {item.DisplayName}";
				}
				break;
			case "status":
			{
				int num = RouletteEvents.Table.Count((RouletteEvent e) => e.Weight > 0);
				List<PlayerInfo> players = ServerActions.OnlinePlayers();
				yield return "master=" + (Plugin.Enabled.Value ? "on" : "off") + " " + $"events={num}/{RouletteEvents.Table.Count} " + $"cooldown={Plugin.CooldownSeconds.Value}s " + $"online={players.Count}";
				foreach (PlayerInfo item2 in players)
				{
					yield return $"  {item2.m_name} at {ServerActions.PositionOf(item2)}";
				}
				break;
			}
			case "possible":
				foreach (string item3 in ListPossibleRaids())
				{
					yield return item3;
				}
				break;
			case "fx":
				foreach (string item4 in PlayEffect(text2))
				{
					yield return item4;
				}
				break;
			case "stopraid":
				yield return ServerActions.StopEvent() ? "cleared the current raid" : "RandEventSystem is not available";
				break;
			case "reload":
				Plugin.ReloadConfig();
				yield return "config reloaded from disk";
				break;
			case "roll":
				foreach (string item5 in FireOne(null))
				{
					yield return item5;
				}
				break;
			case "fire":
				foreach (string item6 in FireOne(text2))
				{
					yield return item6;
				}
				break;
			default:
				yield return "unknown command '" + text + "'; try help";
				break;
			}
		}

		private static IEnumerable<string> ListPossibleRaids()
		{
			List<KeyValuePair<RandomEvent, Vector3>> list = ServerActions.PossibleRaids();
			if (list.Count == 0)
			{
				yield return "no raid is currently valid (nobody online, or none passes the global-key, biome and base checks)";
				yield break;
			}
			foreach (KeyValuePair<RandomEvent, Vector3> item in list)
			{
				yield return $"{item.Key.m_name,-24} at {item.Value}";
			}
		}

		private static IEnumerable<string> PlayEffect(string prefab)
		{
			if (string.IsNullOrEmpty(prefab))
			{
				yield return "usage: fx <prefabName>  (e.g. fx fx_redlightning_burst)";
				yield break;
			}
			List<PlayerInfo> list = ServerActions.OnlinePlayers();
			if (list.Count == 0)
			{
				yield return "nobody is online to show it to";
				yield break;
			}
			PlayerInfo val = list[0];
			Vector3 val2 = ServerActions.PositionOf(val);
			ServerActions.PlayEffect(prefab, val2);
			yield return $"played '{prefab}' at {val.m_name} {val2}";
		}

		private static IEnumerable<string> FireOne(string id)
		{
			List<PlayerInfo> list = ServerActions.OnlinePlayers();
			if (list.Count == 0)
			{
				yield return "nobody is online; effects would have nothing to act on";
				yield break;
			}
			PlayerInfo val = list[0];
			Vector3 val2 = ServerActions.PositionOf(val);
			DeathContext deathContext = new DeathContext(val.m_name, val2, val.m_characterID, isTest: true);
			if (string.IsNullOrEmpty(id) || id.Equals("random", StringComparison.OrdinalIgnoreCase))
			{
				RouletteEvents.RollAndFire(deathContext);
				yield return "rolled the table (victim stand-in: " + val.m_name + ")";
				yield break;
			}
			RouletteEvent rouletteEvent = RouletteEvents.Find(id);
			if (rouletteEvent == null)
			{
				yield return "no event '" + id + "'; try list";
				yield break;
			}
			string text = null;
			string text2 = null;
			try
			{
				text = rouletteEvent.Fire(deathContext);
			}
			catch (Exception ex)
			{
				text2 = ex.Message;
				Plugin.Log.LogError((object)$"Event '{rouletteEvent.Id}' threw: {ex}");
			}
			if (text2 != null)
			{
				yield return rouletteEvent.Id + " threw: " + text2;
				yield break;
			}
			if (text == null)
			{
				yield return rouletteEvent.Id + " declined (nothing for it to act on)";
				yield break;
			}
			Announce.Manual(val.m_name, rouletteEvent, text, val2);
			yield return "fired " + rouletteEvent.Id + ": " + text;
		}
	}
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	internal static class ConsoleCommands
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__Handle;
		}

		private static bool _registered;

		[HarmonyPostfix]
		private static void Postfix()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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)
			//IL_0068: Expected O, but got Unknown
			if (!_registered)
			{
				_registered = true;
				object obj = <>O.<0>__Handle;
				if (obj == null)
				{
					ConsoleEvent val = Handle;
					<>O.<0>__Handle = val;
					obj = (object)val;
				}
				new ConsoleCommand("deathroulette", "DeathRoulette admin: dr <list|status|possible|fire <id>|roll|fx <prefab>|stopraid|reload|help>", (ConsoleEvent)obj, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj2 = <>O.<0>__Handle;
				if (obj2 == null)
				{
					ConsoleEvent val2 = Handle;
					<>O.<0>__Handle = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("dr", "Alias of deathroulette.", (ConsoleEvent)obj2, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				Plugin.Log.LogInfo((object)"Console commands registered: 'deathroulette' / 'dr'.");
			}
		}

		private static void Handle(ConsoleEventArgs args)
		{
			string text = ((args.Length > 1) ? string.Join(" ", args.Args.Skip(1)) : "help");
			try
			{
				foreach (string item in CommandFile.Execute(text))
				{
					Terminal context = args.Context;
					if (context != null)
					{
						context.AddString(item);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"Console command '{text}' threw: {ex}");
				Terminal context2 = args.Context;
				if (context2 != null)
				{
					context2.AddString("DeathRoulette: '" + text + "' threw: " + ex.Message);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class DeathCause
	{
		private readonly struct RecentHit
		{
			public readonly float Time;

			public readonly HitType Type;

			public readonly string Attacker;

			public RecentHit(float time, HitType type, string attacker)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				Time = time;
				Type = type;
				Attacker = attacker;
			}
		}

		private static readonly Dictionary<ZDOID, RecentHit> _recent = new Dictionary<ZDOID, RecentHit>();

		[HarmonyPrefix]
		private static void Prefix(Character __instance, HitData hit)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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)
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !(Plugin.DeathCauseWindowSeconds.Value <= 0f) && !((Object)(object)__instance == (Object)null) && hit != null && __instance.IsPlayer())
			{
				ZDOID zDOID = __instance.GetZDOID();
				if (!((ZDOID)(ref zDOID)).IsNone())
				{
					float realtimeSinceStartup = Time.realtimeSinceStartup;
					Prune(realtimeSinceStartup);
					_recent[zDOID] = new RecentHit(realtimeSinceStartup, hit.m_hitType, AttackerName(hit));
				}
			}
		}

		public static string Describe(ZDOID id)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.DeathCauseWindowSeconds.Value <= 0f || ((ZDOID)(ref id)).IsNone())
			{
				return null;
			}
			if (!_recent.TryGetValue(id, out var value))
			{
				return null;
			}
			_recent.Remove(id);
			if (Time.realtimeSinceStartup - value.Time > Plugin.DeathCauseWindowSeconds.Value)
			{
				return null;
			}
			return Phrase(value.Type, value.Attacker);
		}

		private static string AttackerName(HitData hit)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			Character val = null;
			try
			{
				val = hit.GetAttacker();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("DeathCause: could not resolve the attacker: " + ex.Message));
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Player val2 = (Player)(object)((val is Player) ? val : null);
			string text;
			if (val2 != null)
			{
				ZDOMan instance = ZDOMan.instance;
				ZDO val3 = ((instance != null) ? instance.GetZDO(val.GetZDOID()) : null);
				text = ((val3 == null) ? "" : val3.GetString(ZDOVars.s_playerName, ""));
				if (text.Length == 0)
				{
					text = val2.GetPlayerName();
				}
			}
			else
			{
				text = val.m_name;
			}
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			return text.Replace('_', ' ');
		}

		private static string Phrase(HitType type, string attacker)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected I4, but got Unknown
			switch ((int)type)
			{
			case 0:
				if (attacker == null)
				{
					return null;
				}
				return "killed by " + attacker;
			case 1:
				if (attacker == null)
				{
					return "killed by a monster";
				}
				return "killed by " + attacker;
			case 2:
				if (attacker == null)
				{
					return "killed by another player";
				}
				return "killed by " + attacker;
			case 3:
				return "fell to their death";
			case 4:
				return "drowned";
			case 5:
				return "burned to death";
			case 6:
				return "froze to death";
			case 7:
				return "poisoned to death";
			case 8:
				return "drowned";
			case 9:
				return "choked on smoke";
			case 10:
				return "fell from the edge of the world";
			case 11:
				return "killed on impact";
			case 12:
				return "crushed by a cart";
			case 13:
				return "crushed by a falling tree";
			case 14:
				return "struck down by their own hand";
			case 15:
				return "crushed by collapsing structure";
			case 16:
				return "shot down by a turret";
			case 17:
				return "crushed by a boat";
			case 18:
				return "crushed by falling rock";
			case 19:
				return "launched by a catapult";
			case 20:
				return "burned by drifting embers";
			case 21:
				return "boiled in the ash ocean";
			case 22:
				return "burned in molten lava";
			case 23:
				return "thrown into the incinerator";
			case 24:
				return "crushed by a drawbridge";
			default:
				return null;
			}
		}

		private static void Prune(float now)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			float value = Plugin.DeathCauseWindowSeconds.Value;
			List<ZDOID> list = new List<ZDOID>();
			foreach (KeyValuePair<ZDOID, RecentHit> item in _recent)
			{
				if (now - item.Value.Time > value)
				{
					list.Add(item.Key);
				}
			}
			foreach (ZDOID item2 in list)
			{
				_recent.Remove(item2);
			}
		}
	}
	[HarmonyPatch(typeof(ZRoutedRpc), "HandleRoutedRPC")]
	internal static class DeathWatcher
	{
		private static readonly int OnDeathHash = StringExtensionMethods.GetStableHashCode("OnDeath");

		private static readonly int PlayerPrefabHash = StringExtensionMethods.GetStableHashCode("Player");

		private static float _lastFireTime = -99999f;

		[HarmonyPostfix]
		private static void Postfix(RoutedRPCData data)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: 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_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			if (data == null || data.m_methodHash != OnDeathHash || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ((ZDOID)(ref data.m_targetZDO)).IsNone())
			{
				return;
			}
			ZDOMan instance = ZDOMan.instance;
			ZDO val = ((instance != null) ? instance.GetZDO(data.m_targetZDO) : null);
			if (val == null || val.GetPrefab() != PlayerPrefabHash)
			{
				return;
			}
			string text = val.GetString(ZDOVars.s_playerName, "someone");
			Vector3 position = val.GetPosition();
			if (Plugin.VerboseLogging.Value)
			{
				Plugin.Log.LogInfo((object)$"Death detected: {text} at {position} (zdo {data.m_targetZDO}).");
			}
			RouletteEvents.EnsureDumped();
			DeathContext ctx = new DeathContext(text, position, data.m_targetZDO);
			Announce.Death(ctx);
			if (!Plugin.Enabled.Value)
			{
				return;
			}
			float value = Plugin.CooldownSeconds.Value;
			if (Time.realtimeSinceStartup - _lastFireTime < value)
			{
				if (Plugin.VerboseLogging.Value)
				{
					Plugin.Log.LogInfo((object)$"Roll skipped for {text}: cooldown ({value}s) still active.");
				}
			}
			else
			{
				_lastFireTime = Time.realtimeSinceStartup;
				RouletteEvents.RollAndFire(ctx);
			}
		}
	}
	internal readonly struct DeathContext
	{
		public readonly string PlayerName;

		public readonly Vector3 Position;

		public readonly ZDOID CharacterId;

		public readonly bool IsTest;

		public DeathContext(string playerName, Vector3 position, ZDOID characterId, bool isTest = false)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			PlayerName = playerName;
			Position = position;
			CharacterId = characterId;
			IsTest = isTest;
		}
	}
	internal static class EffectSpec
	{
		private static readonly Random Rng = new Random();

		public static void Play(string spec, Vector3 pos)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			string[] array = PickVariant(spec);
			for (int i = 0; i < array.Length; i++)
			{
				ServerActions.PlayEffect(array[i], pos);
			}
		}

		public static void PlayAtEveryone(string spec)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			string[] array = PickVariant(spec);
			if (array.Length == 0)
			{
				return;
			}
			foreach (PlayerInfo item in ServerActions.OnlinePlayers())
			{
				Vector3 pos = ServerActions.PositionOf(item);
				string[] array2 = array;
				for (int i = 0; i < array2.Length; i++)
				{
					ServerActions.PlayEffect(array2[i], pos);
				}
			}
		}

		private static string[] PickVariant(string spec)
		{
			if (string.IsNullOrEmpty(spec))
			{
				return new string[0];
			}
			string[] array = (from v in spec.Split('|')
				select v.Trim() into v
				where v.Length > 0
				select v).ToArray();
			if (array.Length == 0)
			{
				return new string[0];
			}
			return (from p in array[Rng.Next(array.Length)].Split('+')
				select p.Trim() into p
				where p.Length > 0
				select p).ToArray();
		}
	}
	internal static class EffectSustain
	{
		private const float TickSeconds = 2f;

		public static void Run(IReadOnlyList<string> statusEffects, float durationSeconds, ServerActions.Element? damageElement = null, float damagePerTick = 0f)
		{
			Plugin instance = Plugin.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				((MonoBehaviour)instance).StartCoroutine(Loop(statusEffects, durationSeconds, damageElement, damagePerTick));
			}
		}

		private static IEnumerator Loop(IReadOnlyList<string> statusEffects, float duration, ServerActions.Element? element, float damagePerTick)
		{
			float elapsed = 0f;
			WaitForSeconds wait = new WaitForSeconds(2f);
			while (elapsed <= duration)
			{
				foreach (PlayerInfo item in ServerActions.OnlinePlayers())
				{
					foreach (string statusEffect in statusEffects)
					{
						ServerActions.AddStatusEffect(item.m_characterID, statusEffect);
					}
					if (element.HasValue && damagePerTick > 0f)
					{
						ServerActions.DamageElemental(item.m_characterID, element.Value, damagePerTick);
					}
				}
				elapsed += 2f;
				yield return wait;
			}
		}

		public static void SustainNoise(float radius, float durationSeconds)
		{
			Plugin instance = Plugin.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				((MonoBehaviour)instance).StartCoroutine(NoiseLoop(radius, durationSeconds));
			}
		}

		private static IEnumerator NoiseLoop(float radius, float duration)
		{
			float elapsed = 0f;
			WaitForSeconds wait = new WaitForSeconds(2f);
			while (elapsed <= duration)
			{
				foreach (PlayerInfo item in ServerActions.OnlinePlayers())
				{
					ServerActions.AddNoise(item.m_characterID, radius);
					ServerActions.AttractCreatures(ServerActions.PositionOf(item), radius);
				}
				elapsed += 2f;
				yield return wait;
			}
		}

		public static void Countdown(float seconds, Action action)
		{
			Plugin instance = Plugin.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				try
				{
					action();
					return;
				}
				catch (Exception arg)
				{
					Plugin.Log.LogError((object)$"Countdown action threw: {arg}");
					return;
				}
			}
			((MonoBehaviour)instance).StartCoroutine(CountdownLoop(seconds, action));
		}

		private static IEnumerator CountdownLoop(float seconds, Action action)
		{
			int num = Mathf.Max(1, Mathf.CeilToInt(seconds));
			WaitForSeconds wait = new WaitForSeconds(1f);
			for (int i = num; i > 0; i--)
			{
				ServerActions.ShowMessage((MessageType)2, i.ToString());
				yield return wait;
			}
			try
			{
				action();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Countdown action threw: {arg}");
			}
		}

		public static void Delay(float seconds, Action action)
		{
			Plugin instance = Plugin.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				((MonoBehaviour)instance).StartCoroutine(DelayLoop(seconds, action));
			}
		}

		private static IEnumerator DelayLoop(float seconds, Action action)
		{
			yield return (object)new WaitForSeconds(seconds);
			try
			{
				action();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Delayed action threw: {arg}");
			}
		}

		public static void WaitForRespawn(ZDOID player, float minSeconds, float timeoutSeconds, Action action)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			Plugin instance = Plugin.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				((MonoBehaviour)instance).StartCoroutine(RespawnLoop(player, minSeconds, timeoutSeconds, action));
			}
		}

		private static IEnumerator RespawnLoop(ZDOID player, float minSeconds, float timeoutSeconds, Action action)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			WaitForSeconds wait = new WaitForSeconds(1f);
			float elapsed = 0f;
			while (elapsed < minSeconds || !IsAlive(player))
			{
				elapsed += 1f;
				if (elapsed > timeoutSeconds)
				{
					yield break;
				}
				yield return wait;
			}
			try
			{
				action();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Respawn-delayed action threw: {arg}");
			}
		}

		private static bool IsAlive(ZDOID id)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			ZDOMan instance = ZDOMan.instance;
			ZDO val = ((instance != null) ? instance.GetZDO(id) : null);
			if (val != null)
			{
				return val.GetFloat(ZDOVars.s_health, 0f) > 1f;
			}
			return false;
		}

		public static void WatchForDeath(Vector3 near, float radius, float delaySeconds, Action onDeath)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			Plugin instance = Plugin.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				((MonoBehaviour)instance).StartCoroutine(WatchLoop(near, radius, delaySeconds, onDeath));
			}
		}

		private static IEnumerator WatchLoop(Vector3 near, float radius, float delaySeconds, Action onDeath)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			WaitForSeconds wait = new WaitForSeconds(1f);
			Character target = null;
			while ((Object)(object)target == (Object)null)
			{
				foreach (Character allCharacter in Character.GetAllCharacters())
				{
					if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.IsPlayer() && allCharacter.IsBoss())
					{
						Vector3 val = ((Component)allCharacter).transform.position - near;
						if (!(((Vector3)(ref val)).sqrMagnitude > radius * radius))
						{
							target = allCharacter;
							break;
						}
					}
				}
				yield return wait;
			}
			while ((Object)(object)target != (Object)null && !target.IsDead())
			{
				yield return wait;
			}
			yield return (object)new WaitForSeconds(delaySeconds);
			try
			{
				onDeath();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"WatchForDeath callback threw: {arg}");
			}
		}
	}
	[BepInPlugin("geckuss.deathroulette", "DeathRoulette", "1.6.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "geckuss.deathroulette";

		public const string ModName = "DeathRoulette";

		public const string ModVersion = "1.6.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> AnnounceDeaths;

		internal static ConfigEntry<bool> AnnounceEffects;

		internal static ConfigEntry<float> CooldownSeconds;

		internal static ConfigEntry<float> CountdownSeconds;

		internal static ConfigEntry<float> DeathCauseWindowSeconds;

		internal static ConfigEntry<bool> VerboseLogging;

		private static Plugin _instance;

		private Harmony _harmony;

		internal static Plugin Instance => _instance;

		private void Awake()
		{
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Expected O, but got Unknown
			_instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. When false, deaths are detected but no event fires.");
			AnnounceDeaths = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AnnounceDeaths", true, "Announce who died to everyone. Fires on every death, even when the roulette is disabled or on cooldown.");
			AnnounceEffects = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AnnounceEffects", true, "Announce which event was rolled, and what it did, to everyone.");
			CooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CooldownSeconds", 30f, "Minimum seconds between rolled events, so a wipe does not fire five raids at once.");
			CountdownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CountdownSeconds", 3f, "Seconds of countdown after a death before the rolled event fires. Walks down 3, 2, 1 in the raid-banner slot; 0 disables the delay and fires instantly.");
			DeathCauseWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DeathCauseWindowSeconds", 5f, "How far back a server-visible hit is still named as the cause in the death announcement. A dedicated server only sees server-owned damage (falling trees, carts, creature projectiles, environment AOEs), so most causes stay generic; 0 disables cause attribution.");
			VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "VerboseLogging", false, "Log every detected death and roll.");
			RouletteEvents.BindConfig(((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			CommandFile.Init(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath));
			_harmony = new Harmony("geckuss.deathroulette");
			_harmony.PatchAll();
			Log.LogInfo((object)"DeathRoulette 1.6.0 loaded.");
		}

		private void Update()
		{
			CommandFile.Poll();
		}

		internal static void ReloadConfig()
		{
			Plugin instance = _instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Config.Reload();
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal enum Flavour
	{
		Harmless,
		Kind,
		Mean,
		Brutal
	}
	internal sealed class RouletteEvent
	{
		public string Id;

		public string DisplayName;

		public Flavour Flavour;

		public int DefaultWeight;

		public string Description;

		public Func<DeathContext, string> Fire;

		internal ConfigEntry<int> WeightConfig;

		public int Weight => WeightConfig?.Value ?? DefaultWeight;
	}
	internal static class RouletteEvents
	{
		private sealed class BossEntry
		{
			public readonly string Key;

			public readonly string Altar;

			public readonly string Prefab;

			public readonly string Label;

			public readonly string MusicEvent;

			public BossEntry(string key, string altar, string prefab, string label, string musicEvent)
			{
				Key = key;
				Altar = altar;
				Prefab = prefab;
				Label = label;
				MusicEvent = musicEvent;
			}
		}

		private static readonly Random Rng = new Random();

		private static string _lastFiredId;

		private static bool _dumped;

		private const string DefaultRaids = "";

		private static ConfigEntry<string> _raidPool;

		private static ConfigEntry<bool> _gateRaids;

		private static ConfigEntry<string> _thunderFx;

		private static ConfigEntry<string> _omenFx;

		private static ConfigEntry<string> _lamentFx;

		private static ConfigEntry<float> _thunderDamage;

		private static ConfigEntry<bool> _summonBoss;

		private static ConfigEntry<string> _adrenalineBuff;

		private static ConfigEntry<float> _loudGriefDuration;

		private static ConfigEntry<float> _loudGriefPower;

		private static ConfigEntry<int> _loudGriefSpawns;

		private static ConfigEntry<string> _loudGriefSpawnPrefab;

		private static readonly string[] UngatedFallback = new string[9] { "army_eikthyr", "army_theelder", "foresttrolls", "blobs", "skeletons", "surtlings", "wolves", "bats", "ghosts" };

		private static readonly string[] ForsakenPowers = new string[7] { "GP_Eikthyr", "GP_TheElder", "GP_Bonemass", "GP_Moder", "GP_Yagluth", "GP_Queen", "GP_Fader" };

		private static readonly string[] Potions = new string[7] { "Potion_barleywine", "Potion_frostresist", "Potion_poisonresist", "Potion_LightFoot", "Potion_eitr_minor", "Potion_TrollPheromones", "Potion_stamina_medium" };

		private static readonly string[] ArmorSetEffects = new string[7] { "SetEffect_FenringArmor", "SetEffect_BerserkerArmor", "SetEffect_WolfArmor", "SetEffect_RootArmor", "SetEffect_TrollArmor", "SetEffect_LoxArmor", "SetEffect_HarvesterArmor" };

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

		private const string DefaultThunderFx = "fx_redlightning_burst+fx_chainlightning_hit+fx_lightningweapon_hit|fx_JotunWitch_LightningBolt_Explosion+fx_himminafl_aoe";

		private static readonly string[] Windfall = new string[24]
		{
			"Resin", "Wood", "Stone", "Flint", "Feathers", "Mushroom", "Raspberry", "Honey", "Coins", "BoneFragments",
			"RoundLog", "FineWood", "Coal", "LeatherScraps", "DeerHide", "WolfPelt", "GreydwarfEye", "Dandelion", "Thistle", "Blueberries",
			"Cloudberry", "Carrot", "Turnip", "SurtlingCore"
		};

		private static readonly string[] SwarmCreatures = new string[5] { "Greyling", "Neck", "Boar", "Skeleton", "Greydwarf" };

		private static readonly (string Location, string Label, PinType Pin, int Weight)[] Landmarks = new(string, string, PinType, int)[23]
		{
			("Crypt2", "Burial Chamber", (PinType)3, 4),
			("Crypt3", "Burial Chamber", (PinType)3, 4),
			("Crypt4", "Burial Chamber", (PinType)3, 4),
			("SunkenCrypt1", "Sunken Crypt", (PinType)3, 4),
			("SunkenCrypt2", "Sunken Crypt", (PinType)3, 4),
			("SunkenCrypt3", "Sunken Crypt", (PinType)3, 4),
			("SunkenCrypt4", "Sunken Crypt", (PinType)3, 4),
			("MountainCave02", "Frost Cave", (PinType)3, 4),
			("TrollCave", "Troll Cave", (PinType)3, 4),
			("TrollCave02", "Troll Cave", (PinType)3, 4),
			("FireHole", "Surtling Pit", (PinType)3, 4),
			("BearCave", "Bear Cave", (PinType)3, 4),
			("Vendor_BlackForest", "Haldor", (PinType)6, 2),
			("Hildir_camp", "Hildir", (PinType)6, 2),
			("BogWitch_Camp", "Bog Witch", (PinType)6, 2),
			("Hildir_cave", "Hildir's Cave", (PinType)3, 2),
			("Hildir_crypt", "Hildir's Crypt", (PinType)3, 2),
			("Hildir_plainsfortress", "Hildir's Fortress", (PinType)3, 2),
			("Eikthyrnir", "Eikthyr", (PinType)9, 1),
			("GDKing", "The Elder", (PinType)9, 1),
			("Bonemass", "Bonemass", (PinType)9, 1),
			("Dragonqueen", "Moder", (PinType)9, 1),
			("GoblinKing", "Yagluth", (PinType)9, 1)
		};

		private static string _lastPinnedType;

		private static List<Vector3> _lastPinnedPins;

		private static readonly BossEntry[] Bosses = new BossEntry[5]
		{
			new BossEntry("defeated_eikthyr", "Eikthyrnir", "Eikthyr", "Eikthyr", "boss_eikthyr"),
			new BossEntry("defeated_gdking", "GDKing", "gd_king", "The Elder", "boss_gdking"),
			new BossEntry("defeated_bonemass", "Bonemass", "Bonemass", "Bonemass", "boss_bonemass"),
			new BossEntry("defeated_dragon", "Dragonqueen", "Dragon", "Moder", "boss_moder"),
			new BossEntry("defeated_goblinking", "GoblinKing", "GoblinKing", "Yagluth", "boss_goblinking")
		};

		private static readonly string[] PhantomWords = new string[18]
		{
			"MOURNED", "NOTED", "TYPICAL", "AGAIN?", "SKILL ISSUE", "VALHALLA", "OOPS", "RECORDED", "GET GOOD", "COOKED",
			"FUMBLED", "GREEDY", "TROLLED", "UNLUCKY", "NOT TODAY", "OUCH", "GLITCHED?", "LEGENDARY FAIL"
		};

		private const string DefaultOmenFx = "sfx_frostfoundry_activate";

		private const string DefaultLamentFx = "sfx_gjall_alerted+sfx_gjall_idle_vocals";

		internal static readonly List<RouletteEvent> Table = new List<RouletteEvent>
		{
			new RouletteEvent
			{
				Id = "taunt",
				DisplayName = "Odin's Indifference",
				Flavour = Flavour.Harmless,
				DefaultWeight = 9,
				Description = "Odin passes comment. Nothing else happens.",
				Fire = delegate
				{
					string[] array = new string[4] { "Odin was not impressed.", "Gone to feast early.", "The Valkyries looked, and kept walking.", "The ground was harder than expected." };
					return array[Rng.Next(array.Length)];
				}
			},
			new RouletteEvent
			{
				Id = "thunderclap",
				DisplayName = "Thunderclap",
				Flavour = Flavour.Mean,
				DefaultWeight = 7,
				Description = "Lightning strikes every player, for a little damage.",
				Fire = delegate
				{
					//IL_0037: Unknown result type (might be due to invalid IL or missing references)
					//IL_003c: Unknown result type (might be due to invalid IL or missing references)
					EffectSpec.PlayAtEveryone(ThunderFx);
					float num = _thunderDamage?.Value ?? 10f;
					if (num > 0f)
					{
						foreach (PlayerInfo item in ServerActions.OnlinePlayers())
						{
							ServerActions.DamageElemental(item.m_characterID, ServerActions.Element.Lightning, num);
						}
					}
					return "The sky cracks, and it finds everyone.";
				}
			},
			new RouletteEvent
			{
				Id = "bossstone_omen",
				DisplayName = "Ancient Omen",
				Flavour = Flavour.Harmless,
				DefaultWeight = 7,
				Description = "A boss-stone activation effect fires at everyone's feet. Pure theatre.",
				Fire = delegate
				{
					EffectSpec.PlayAtEveryone(_omenFx?.Value ?? "sfx_frostfoundry_activate");
					return "Something ancient noticed.";
				}
			},
			new RouletteEvent
			{
				Id = "gjall_lament",
				DisplayName = "Lament",
				Flavour = Flavour.Harmless,
				DefaultWeight = 7,
				Description = "A gjall's taunt echoes over every player. Unsettling, harmless.",
				Fire = delegate
				{
					EffectSpec.PlayAtEveryone(_lamentFx?.Value ?? "sfx_gjall_alerted+sfx_gjall_idle_vocals");
					return "A mourning cry answers from far off.";
				}
			},
			new RouletteEvent
			{
				Id = "forsaken_gift",
				DisplayName = "Forsaken Gift",
				Flavour = Flavour.Kind,
				DefaultWeight = 7,
				Description = "A random forsaken power is granted to everyone.",
				Fire = delegate
				{
					string text = ForsakenPowers[Rng.Next(ForsakenPowers.Length)];
					ServerActions.AddStatusEffectToAll(text);
					return "A boon is granted to all: " + text + ".";
				}
			},
			new RouletteEvent
			{
				Id = "rested",
				DisplayName = "Granted Rest",
				Flavour = Flavour.Kind,
				DefaultWeight = 4,
				Description = "Rested buff for everyone.",
				Fire = delegate
				{
					ServerActions.AddStatusEffectToAll("Rested");
					return "The gods grant rest to all.";
				}
			},
			new RouletteEvent
			{
				Id = "second_wind",
				DisplayName = "Second Wind",
				Flavour = Flavour.Kind,
				DefaultWeight = 6,
				Description = "Every surviving player is healed to full.",
				Fire = delegate(DeathContext ctx)
				{
					//IL_001c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0021: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = Survivors(ctx);
					if (list.Count == 0)
					{
						return (string)null;
					}
					foreach (PlayerInfo item2 in list)
					{
						ServerActions.Heal(item2.m_characterID, 9999f);
					}
					return "Wounds close on the living.";
				}
			},
			new RouletteEvent
			{
				Id = "dawn_mercy",
				DisplayName = "Mercy of Dawn",
				Flavour = Flavour.Kind,
				DefaultWeight = 4,
				Description = "Time skips to the next morning.",
				Fire = (DeathContext ctx) => (!ServerActions.SkipToMorning()) ? null : "The night ends early, out of pity."
			},
			new RouletteEvent
			{
				Id = "dusk_mercy",
				DisplayName = "Mercy of Dusk",
				Flavour = Flavour.Kind,
				DefaultWeight = 4,
				Description = "Time skips to the next nightfall.",
				Fire = (DeathContext ctx) => (!ServerActions.SkipToNight()) ? null : "The daylight scatters and the dark rushes in."
			},
			new RouletteEvent
			{
				Id = "soaked",
				DisplayName = "Cold Rain",
				Flavour = Flavour.Mean,
				DefaultWeight = 3,
				Description = "A cold downpour: Wet and Cold on everyone, with a splash underfoot.",
				Fire = delegate
				{
					//IL_000f: Unknown result type (might be due to invalid IL or missing references)
					//IL_0014: Unknown result type (might be due to invalid IL or missing references)
					//IL_0015: Unknown result type (might be due to invalid IL or missing references)
					//IL_001a: Unknown result type (might be due to invalid IL or missing references)
					//IL_0020: Unknown result type (might be due to invalid IL or missing references)
					//IL_002b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0031: Unknown result type (might be due to invalid IL or missing references)
					//IL_0032: Unknown result type (might be due to invalid IL or missing references)
					//IL_0042: Unknown result type (might be due to invalid IL or missing references)
					foreach (PlayerInfo item3 in ServerActions.OnlinePlayers())
					{
						Vector3 pos = ServerActions.PositionOf(item3);
						ServerActions.PlayEffect("vfx_watersplash_longship", pos);
						ServerActions.PlayEffect("vfx_Wet", pos);
						ServerActions.AddStatusEffect(item3.m_characterID, "Wet");
						ServerActions.AddStatusEffect(item3.m_characterID, "Cold");
					}
					return "A cold rain falls on everyone.";
				}
			},
			new RouletteEvent
			{
				Id = "blood_tax",
				DisplayName = "Blood Tax",
				Flavour = Flavour.Mean,
				DefaultWeight = 5,
				Description = "Survivors lose up to a quarter of current health. Never lethal.",
				Fire = delegate(DeathContext ctx)
				{
					//IL_001c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0021: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = Survivors(ctx);
					if (list.Count == 0)
					{
						return (string)null;
					}
					foreach (PlayerInfo item4 in list)
					{
						ServerActions.DamageCapped(item4.m_characterID, 40f);
					}
					return "The debt is billed to the living.";
				}
			},
			new RouletteEvent
			{
				Id = "stagger_all",
				DisplayName = "Lurch",
				Flavour = Flavour.Mean,
				DefaultWeight = 5,
				Description = "Everyone is staggered, losing control briefly.",
				Fire = delegate
				{
					//IL_000f: Unknown result type (might be due to invalid IL or missing references)
					//IL_0014: Unknown result type (might be due to invalid IL or missing references)
					//IL_0019: Unknown result type (might be due to invalid IL or missing references)
					foreach (PlayerInfo item5 in ServerActions.OnlinePlayers())
					{
						ServerActions.Stagger(item5.m_characterID, RandomHorizontal());
					}
					return "The ground lurches underfoot.";
				}
			},
			new RouletteEvent
			{
				Id = "loud_grief",
				DisplayName = "Loud Grief",
				Flavour = Flavour.Mean,
				DefaultWeight = 4,
				Description = "Everyone is loud for a minute, drawing creatures in from across the biome -- and wringing a few out of the soil itself.",
				Fire = delegate
				{
					//IL_0099: Unknown result type (might be due to invalid IL or missing references)
					//IL_009e: Unknown result type (might be due to invalid IL or missing references)
					//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
					//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
					//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
					//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
					//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
					//IL_00da: Unknown result type (might be due to invalid IL or missing references)
					//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					float num = _loudGriefDuration?.Value ?? 60f;
					if (num > 0f)
					{
						EffectSustain.SustainNoise(_loudGriefPower?.Value ?? 40f, num);
					}
					int num2 = 0;
					int num3 = _loudGriefSpawns?.Value ?? 2;
					string text = _loudGriefSpawnPrefab?.Value ?? "Greyling";
					if (num3 > 0 && text.Length > 0)
					{
						foreach (PlayerInfo item6 in ServerActions.OnlinePlayers())
						{
							Vector3 val = ServerActions.PositionOf(item6);
							for (int i = 0; i < num3; i++)
							{
								Vector3 pos = val + RandomHorizontal() * (float)(3.0 + Rng.NextDouble() * 4.0);
								if (ServerActions.SpawnNetworked(text, pos))
								{
									num2++;
								}
							}
						}
					}
					string text2 = ((num2 > 0) ? $" {num2} x {text} surface." : "");
					return "Grief carries further than it should." + text2;
				}
			},
			new RouletteEvent
			{
				Id = "raid",
				DisplayName = "Raid",
				Flavour = Flavour.Brutal,
				DefaultWeight = 10,
				Description = "A real raid: music, banner, spawners. Progression-gated by default.",
				Fire = FireRaid
			},
			new RouletteEvent
			{
				Id = "summon_mourners",
				DisplayName = "Summoned Mourners",
				Flavour = Flavour.Brutal,
				DefaultWeight = 5,
				Description = "Every survivor is teleported to the corpse, wherever it is.",
				Fire = delegate(DeathContext ctx)
				{
					//IL_001c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0021: Unknown result type (might be due to invalid IL or missing references)
					//IL_0027: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = Survivors(ctx);
					if (list.Count == 0)
					{
						return (string)null;
					}
					foreach (PlayerInfo item7 in list)
					{
						ServerActions.TeleportTo(item7.m_characterID, ctx.Position);
					}
					return "All of Midgard is summoned to mourn.";
				}
			},
			new RouletteEvent
			{
				Id = "scatter",
				DisplayName = "Scattered",
				Flavour = Flavour.Brutal,
				DefaultWeight = 7,
				Description = "Every survivor is thrown a short random distance.",
				Fire = delegate(DeathContext ctx)
				{
					//IL_001c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0021: Unknown result type (might be due to invalid IL or missing references)
					//IL_0035: Unknown result type (might be due to invalid IL or missing references)
					//IL_003a: Unknown result type (might be due to invalid IL or missing references)
					//IL_003b: Unknown result type (might be due to invalid IL or missing references)
					//IL_003c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0041: Unknown result type (might be due to invalid IL or missing references)
					//IL_0042: Unknown result type (might be due to invalid IL or missing references)
					//IL_0047: Unknown result type (might be due to invalid IL or missing references)
					//IL_0048: Unknown result type (might be due to invalid IL or missing references)
					//IL_0049: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = Survivors(ctx);
					if (list.Count == 0)
					{
						return (string)null;
					}
					foreach (PlayerInfo item8 in list)
					{
						Vector3 val = RandomHorizontal() * Random.Range(20f, 60f);
						ServerActions.TeleportTo(pos: ServerActions.PositionOf(item8) + val, target: item8.m_characterID);
					}
					return "The living are scattered.";
				}
			},
			new RouletteEvent
			{
				Id = "grave_guardian",
				DisplayName = "Grave Guardian",
				Flavour = Flavour.Brutal,
				DefaultWeight = 7,
				Description = "One creature is spawned on the corpse to guard it.",
				Fire = delegate(DeathContext ctx)
				{
					//IL_0041: Unknown result type (might be due to invalid IL or missing references)
					string[] array = new string[5] { "Draugr", "Greydwarf_Elite", "Skeleton", "Wraith", "Ghost" };
					string text = array[Rng.Next(array.Length)];
					return (!ServerActions.SpawnNetworked(text, ctx.Position)) ? null : ("Something now guards the grave: " + text + ".");
				}
			},
			new RouletteEvent
			{
				Id = "corpse_run",
				DisplayName = "Called to the Grave",
				Flavour = Flavour.Kind,
				DefaultWeight = 5,
				Description = "Fifteen seconds after dying -- once the fallen player has respawned -- they are pulled back to their grave.",
				Fire = delegate(DeathContext ctx)
				{
					//IL_000f: Unknown result type (might be due to invalid IL or missing references)
					//IL_0014: Unknown result type (might be due to invalid IL or missing references)
					//IL_0022: Unknown result type (might be due to invalid IL or missing references)
					//IL_0027: Unknown result type (might be due to invalid IL or missing references)
					//IL_002e: Unknown result type (might be due to invalid IL or missing references)
					//IL_0033: Unknown result type (might be due to invalid IL or missing references)
					//IL_0039: Unknown result type (might be due to invalid IL or missing references)
					if (!ctx.IsTest)
					{
						ZDOID characterId = ctx.CharacterId;
						if (!((ZDOID)(ref characterId)).IsNone())
						{
							ZDOID who = ctx.CharacterId;
							Vector3 grave = ctx.Position;
							EffectSustain.WaitForRespawn(who, 15f, 300f, delegate
							{
								//IL_0001: Unknown result type (might be due to invalid IL or missing references)
								//IL_0007: Unknown result type (might be due to invalid IL or missing references)
								//IL_0018: Unknown result type (might be due to invalid IL or missing references)
								ServerActions.TeleportTo(who, grave);
								ServerActions.PlayEffect("vfx_spawn", grave);
							});
							return "The grave calls its owner back.";
						}
					}
					return (string)null;
				}
			},
			new RouletteEvent
			{
				Id = "revealed_path",
				DisplayName = "A Path Revealed",
				Flavour = Flavour.Kind,
				DefaultWeight = 4,
				Description = "Pins the nearest landmark on each player's map and turns them to face it.",
				Fire = delegate
				{
					//IL_006b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0070: Unknown result type (might be due to invalid IL or missing references)
					//IL_0072: Unknown result type (might be due to invalid IL or missing references)
					//IL_0074: Unknown result type (might be due to invalid IL or missing references)
					//IL_0087: Unknown result type (might be due to invalid IL or missing references)
					//IL_0089: Unknown result type (might be due to invalid IL or missing references)
					//IL_008e: Unknown result type (might be due to invalid IL or missing references)
					//IL_00b6: 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_00f1: Unknown result type (might be due to invalid IL or missing references)
					//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
					//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = ServerActions.OnlinePlayers();
					if (list.Count == 0)
					{
						return (string)null;
					}
					(string, string, PinType, int)[] array = Landmarks;
					while (array.Length != 0)
					{
						(string Location, string Label, PinType Pin, int Weight) mark = WeightedLandmark(array);
						Vector3[] array2 = ((mark.Location == _lastPinnedType) ? _lastPinnedPins.ToArray() : null);
						int num = 0;
						List<Vector3> list2 = new List<Vector3>();
						foreach (PlayerInfo item9 in list)
						{
							long num2 = ServerActions.OwnerOf(item9.m_characterID);
							if (num2 != 0L)
							{
								Vector3 near = ServerActions.PositionOf(item9);
								Vector3[] avoid = ((array2 == null) ? list2.ToArray() : list2.Concat(array2).ToArray());
								if (ServerActions.FindLocation(mark.Location, near, avoid, out var pos) || ServerActions.FindLocation(mark.Location, near, out pos))
								{
									ServerActions.RevealPosition(num2, mark.Label, mark.Pin, pos);
									list2.Add(pos);
									num++;
								}
							}
						}
						if (num != 0)
						{
							_lastPinnedType = mark.Location;
							_lastPinnedPins = list2;
							return "The dead whisper directions: " + mark.Label + ".";
						}
						array = array.Where(((string Location, string Label, PinType Pin, int Weight) l) => l.Location != mark.Location).ToArray();
					}
					return (string)null;
				}
			},
			new RouletteEvent
			{
				Id = "windfall",
				DisplayName = "Windfall",
				Flavour = Flavour.Kind,
				DefaultWeight = 7,
				Description = "Scatters a pile of materials on the ground at the corpse.",
				Fire = delegate(DeathContext ctx)
				{
					//IL_002d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0032: Unknown result type (might be due to invalid IL or missing references)
					//IL_004c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0051: Unknown result type (might be due to invalid IL or missing references)
					//IL_0056: Unknown result type (might be due to invalid IL or missing references)
					//IL_0060: Unknown result type (might be due to invalid IL or missing references)
					//IL_0065: Unknown result type (might be due to invalid IL or missing references)
					//IL_006a: Unknown result type (might be due to invalid IL or missing references)
					//IL_006d: Unknown result type (might be due to invalid IL or missing references)
					string text = Windfall[Rng.Next(Windfall.Length)];
					int num = Rng.Next(5, 9);
					int num2 = 0;
					for (int i = 0; i < num; i++)
					{
						Vector3 pos = ctx.Position + RandomHorizontal() * (float)(Rng.NextDouble() * 2.0) + Vector3.up * 0.5f;
						if (ServerActions.SpawnNetworked(text, pos))
						{
							num2++;
						}
					}
					return (num2 != 0) ? $"The ground gives up {num2} x {text}." : null;
				}
			},
			new RouletteEvent
			{
				Id = "gods_relent",
				DisplayName = "The Gods Relent",
				Flavour = Flavour.Kind,
				DefaultWeight = 4,
				Description = "Ends the raid that is currently running. Declines when there is none.",
				Fire = delegate
				{
					if (!ServerActions.HaveActiveRaid())
					{
						return (string)null;
					}
					return (!ServerActions.StopEvent()) ? null : "The attack loses interest and melts away.";
				}
			},
			new RouletteEvent
			{
				Id = "phantom_numbers",
				DisplayName = "Phantom Numbers",
				Flavour = Flavour.Harmless,
				DefaultWeight = 6,
				Description = "Floating text appears over every player. Purely cosmetic.",
				Fire = delegate
				{
					//IL_0033: Unknown result type (might be due to invalid IL or missing references)
					//IL_0038: Unknown result type (might be due to invalid IL or missing references)
					//IL_003d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0047: Unknown result type (might be due to invalid IL or missing references)
					//IL_004c: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = ServerActions.OnlinePlayers();
					if (list.Count == 0)
					{
						return (string)null;
					}
					string text = PhantomWords[Rng.Next(PhantomWords.Length)];
					foreach (PlayerInfo item10 in list)
					{
						ServerActions.FloatingText(ServerActions.PositionOf(item10) + Vector3.up * 1.8f, text, (TextType)7);
					}
					return "The air itself keeps score.";
				}
			},
			new RouletteEvent
			{
				Id = "deep_chill",
				DisplayName = "Deep Chill",
				Flavour = Flavour.Mean,
				DefaultWeight = 4,
				Description = "Cold and Freezing on everyone for 10 seconds, with a steady frost bite.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[2] { "Cold", "Freezing" }, 10f, ServerActions.Element.Frost, 3f);
					return "The cold comes for the living too.";
				}
			},
			new RouletteEvent
			{
				Id = "tar_soaked",
				DisplayName = "Tar-Soaked",
				Flavour = Flavour.Mean,
				DefaultWeight = 4,
				Description = "Tared on everyone for 10 seconds: slow and clumsy.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { "Tared" }, 10f);
					return "Something clinging and black coats every boot.";
				}
			},
			new RouletteEvent
			{
				Id = "slimed",
				DisplayName = "Slimed",
				Flavour = Flavour.Mean,
				DefaultWeight = 3,
				Description = "Slimed on everyone for 20 seconds: slow and clumsy. Split out from Tar-Soaked so it can be disabled on its own.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { "Slimed" }, 20f);
					return "A sticky film clings to every boot and hand.";
				}
			},
			new RouletteEvent
			{
				Id = "swarm",
				DisplayName = "Swarm",
				Flavour = Flavour.Brutal,
				DefaultWeight = 8,
				Description = "Spawns a handful of small creatures on the corpse.",
				Fire = delegate(DeathContext ctx)
				{
					//IL_002c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0031: Unknown result type (might be due to invalid IL or missing references)
					//IL_0055: Unknown result type (might be due to invalid IL or missing references)
					//IL_005a: Unknown result type (might be due to invalid IL or missing references)
					//IL_005f: 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)
					//IL_0082: Unknown result type (might be due to invalid IL or missing references)
					string text = SwarmCreatures[Rng.Next(SwarmCreatures.Length)];
					int num = Rng.Next(3, 6);
					int num2 = 0;
					for (int i = 0; i < num; i++)
					{
						Vector3 pos = ctx.Position + RandomHorizontal() * (float)(2.0 + Rng.NextDouble() * 3.0);
						if (ServerActions.SpawnNetworked(text, pos))
						{
							num2++;
						}
					}
					if (num2 == 0)
					{
						return (string)null;
					}
					ServerActions.PlayEffect("vfx_spawn_large", ctx.Position);
					return $"{num2} x {text} crawl out to meet the body.";
				}
			},
			new RouletteEvent
			{
				Id = "riktig_fuling",
				DisplayName = "Riktig Fuling",
				Flavour = Flavour.Brutal,
				DefaultWeight = 6,
				Description = "A band of goblin_gem -- real, undressed Fulings -- spawns on the corpse.",
				Fire = delegate(DeathContext ctx)
				{
					//IL_0014: Unknown result type (might be due to invalid IL or missing references)
					//IL_0019: Unknown result type (might be due to invalid IL or missing references)
					//IL_003d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0042: Unknown result type (might be due to invalid IL or missing references)
					//IL_0047: Unknown result type (might be due to invalid IL or missing references)
					//IL_004d: Unknown result type (might be due to invalid IL or missing references)
					//IL_006c: Unknown result type (might be due to invalid IL or missing references)
					int num = Rng.Next(2, 5);
					int num2 = 0;
					for (int i = 0; i < num; i++)
					{
						Vector3 pos = ctx.Position + RandomHorizontal() * (float)(2.0 + Rng.NextDouble() * 3.0);
						if (ServerActions.SpawnNetworked("Goblin_Gem", pos))
						{
							num2++;
						}
					}
					if (num2 == 0)
					{
						return (string)null;
					}
					ServerActions.PlayEffect("vfx_spawn_large", ctx.Position);
					return $"{num2} x Goblin_Gem (riktig fulings) come to strip the grave.";
				}
			},
			new RouletteEvent
			{
				Id = "no_escape",
				DisplayName = "No Escape",
				Flavour = Flavour.Brutal,
				DefaultWeight = 5,
				Description = "Portals refuse to work for five minutes. Vanilla's TeleportWorld checks the NoPortals key on every use, so the message is stock.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					ServerActions.SetGlobalKey("NoPortals");
					EffectSustain.Delay(300f, delegate
					{
						ServerActions.RemoveGlobalKey("NoPortals");
					});
					return "The gates are sealed. Nobody travels anywhere.";
				}
			},
			new RouletteEvent
			{
				Id = "swap_places",
				DisplayName = "Swapped",
				Flavour = Flavour.Brutal,
				DefaultWeight = 3,
				Description = "Two random players trade positions. Needs two online.",
				Fire = delegate
				{
					//IL_003f: Unknown result type (might be due to invalid IL or missing references)
					//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_004c: Unknown result type (might be due to invalid IL or missing references)
					//IL_004d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0052: Unknown result type (might be due to invalid IL or missing references)
					//IL_0054: Unknown result type (might be due to invalid IL or missing references)
					//IL_0055: Unknown result type (might be due to invalid IL or missing references)
					//IL_005a: Unknown result type (might be due to invalid IL or missing references)
					//IL_005c: Unknown result type (might be due to invalid IL or missing references)
					//IL_005d: 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)
					//IL_006a: 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_0070: Unknown result type (might be due to invalid IL or missing references)
					//IL_0082: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = ServerActions.OnlinePlayers();
					if (list.Count < 2)
					{
						return (string)null;
					}
					int num = Rng.Next(list.Count);
					int num2 = Rng.Next(list.Count - 1);
					if (num2 >= num)
					{
						num2++;
					}
					PlayerInfo val = list[num];
					PlayerInfo val2 = list[num2];
					Vector3 pos = ServerActions.PositionOf(val);
					ServerActions.TeleportTo(pos: ServerActions.PositionOf(val2), target: val.m_characterID);
					ServerActions.TeleportTo(val2.m_characterID, pos);
					return val.m_name + " and " + val2.m_name + " wake up somewhere unfamiliar.";
				}
			},
			new RouletteEvent
			{
				Id = "feather_fall",
				DisplayName = "Feather Fall",
				Flavour = Flavour.Kind,
				DefaultWeight = 4,
				Description = "Slow Fall on everyone for five minutes: no fall damage.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { "SlowFall" }, 300f);
					return "Everyone is suddenly very light.";
				}
			},
			new RouletteEvent
			{
				Id = "mistveil",
				DisplayName = "Mistveil",
				Flavour = Flavour.Kind,
				DefaultWeight = 3,
				Description = "Demister on everyone for five minutes: the mist parts.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { "Demister" }, 300f);
					return "The mist draws back.";
				}
			},
			new RouletteEvent
			{
				Id = "adrenaline_rush",
				DisplayName = "Adrenaline",
				Flavour = Flavour.Kind,
				DefaultWeight = 4,
				Description = "A surge for everyone: full heal, a stamina-regen buff, and Ashlands adrenaline.",
				Fire = delegate
				{
					//IL_001b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0020: Unknown result type (might be due to invalid IL or missing references)
					//IL_0021: Unknown result type (might be due to invalid IL or missing references)
					//IL_0046: Unknown result type (might be due to invalid IL or missing references)
					//IL_0047: Unknown result type (might be due to invalid IL or missing references)
					//IL_0057: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = ServerActions.OnlinePlayers();
					if (list.Count == 0)
					{
						return (string)null;
					}
					foreach (PlayerInfo item11 in list)
					{
						ServerActions.AddStatusEffect(item11.m_characterID, _adrenalineBuff?.Value ?? "Potion_stamina_medium");
						ServerActions.Heal(item11.m_characterID, 25f);
						ServerActions.AddAdrenaline(item11.m_characterID, 30f);
					}
					return "Hearts hammer. Second wind for everyone.";
				}
			},
			new RouletteEvent
			{
				Id = "pyre",
				DisplayName = "Pyre",
				Flavour = Flavour.Mean,
				DefaultWeight = 4,
				Description = "Burning and fire damage on everyone for 10 seconds, with flames underfoot.",
				Fire = delegate
				{
					//IL_001b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0020: Unknown result type (might be due to invalid IL or missing references)
					//IL_0026: Unknown result type (might be due to invalid IL or missing references)
					//IL_0027: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = ServerActions.OnlinePlayers();
					if (list.Count == 0)
					{
						return (string)null;
					}
					foreach (PlayerInfo item12 in list)
					{
						ServerActions.PlayEffect("fx_fireskeleton_nova", ServerActions.PositionOf(item12));
					}
					EffectSustain.Run(new string[1] { "Burning" }, 10f, ServerActions.Element.Fire, 6f);
					return "The pyre is lit under everyone at once.";
				}
			},
			new RouletteEvent
			{
				Id = "plague",
				DisplayName = "Plague",
				Flavour = Flavour.Mean,
				DefaultWeight = 4,
				Description = "Poison and poison damage on everyone for 20 seconds.",
				Fire = delegate
				{
					//IL_001b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0020: Unknown result type (might be due to invalid IL or missing references)
					//IL_0026: Unknown result type (might be due to invalid IL or missing references)
					//IL_0027: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = ServerActions.OnlinePlayers();
					if (list.Count == 0)
					{
						return (string)null;
					}
					foreach (PlayerInfo item13 in list)
					{
						ServerActions.PlayEffect("vfx_BombBlob_explode_poison", ServerActions.PositionOf(item13));
					}
					EffectSustain.Run(new string[1] { "Poison" }, 20f, ServerActions.Element.Poison, 5f);
					return "Something rotten spreads from the body.";
				}
			},
			new RouletteEvent
			{
				Id = "fog_of_war",
				DisplayName = "Fog of War",
				Flavour = Flavour.Mean,
				DefaultWeight = 3,
				Description = "The map is hidden from everyone for five minutes. The NoMap global key reaches every client, so the minimap and the full map go dark.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					ServerActions.SetGlobalKey("NoMap");
					EffectSustain.Delay(300f, delegate
					{
						ServerActions.RemoveGlobalKey("NoMap");
					});
					return "The world forgets where it is.";
				}
			},
			new RouletteEvent
			{
				Id = "pilgrimage",
				DisplayName = "Pilgrimage",
				Flavour = Flavour.Brutal,
				DefaultWeight = 1,
				Description = "Sends everyone to the altar of the last boss the world defeated, and wakes it up again. Returns everyone home 20 seconds after the boss falls. The single most dangerous event here.",
				Fire = delegate(DeathContext ctx)
				{
					//IL_0056: Unknown result type (might be due to invalid IL or missing references)
					//IL_007d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0082: Unknown result type (might be due to invalid IL or missing references)
					//IL_008a: Unknown result type (might be due to invalid IL or missing references)
					//IL_008c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0091: Unknown result type (might be due to invalid IL or missing references)
					//IL_0093: Unknown result type (might be due to invalid IL or missing references)
					//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
					//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
					//IL_00d1: 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)
					//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
					//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
					//IL_013b: 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_0146: 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_0160: 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_0173: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = ServerActions.OnlinePlayers();
					if (list.Count == 0)
					{
						return (string)null;
					}
					BossEntry bossEntry = null;
					BossEntry[] bosses = Bosses;
					foreach (BossEntry bossEntry2 in bosses)
					{
						if (ServerActions.HasGlobalKey(bossEntry2.Key))
						{
							bossEntry = bossEntry2;
						}
					}
					if (bossEntry == null)
					{
						return (string)null;
					}
					if (!ServerActions.FindLocation(bossEntry.Altar, ctx.Position, out var pos))
					{
						return (string)null;
					}
					List<(ZDOID id, Vector3 pos)> origins = new List<(ZDOID, Vector3)>();
					foreach (PlayerInfo item14 in list)
					{
						origins.Add((item14.m_characterID, ServerActions.PositionOf(item14)));
					}
					foreach (PlayerInfo item15 in list)
					{
						ServerActions.TeleportTo(item15.m_characterID, pos + RandomHorizontal() * (float)(2.0 + Rng.NextDouble() * 4.0));
					}
					bool flag = false;
					if (_summonBoss == null || _summonBoss.Value)
					{
						flag = ServerActions.SpawnNetworked(bossEntry.Prefab, pos + Vector3.up * 1f);
						if (flag)
						{
							ServerActions.PlayEffect("vfx_spawn_large", pos);
							ServerActions.StartEvent(bossEntry.MusicEvent, pos);
							EffectSustain.WatchForDeath(pos, 100f, 20f, delegate
							{
								//IL_0027: Unknown result type (might be due to invalid IL or missing references)
								//IL_002c: Unknown result type (might be due to invalid IL or missing references)
								//IL_002d: Unknown result type (might be due to invalid IL or missing references)
								//IL_0032: Unknown result type (might be due to invalid IL or missing references)
								//IL_0033: 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)
								ServerActions.StopEvent();
								ServerActions.ShowMessage((MessageType)2, "The pilgrimage ends. Everyone returns home.");
								foreach (var (target, pos2) in origins)
								{
									ServerActions.TeleportTo(target, pos2);
								}
							});
						}
					}
					return (!flag) ? ("Everyone is summoned to the altar of " + bossEntry.Label + ".") : (bossEntry.Label + " is dragged back to its altar, and so is everyone else.");
				}
			},
			new RouletteEvent
			{
				Id = "fenris_blessing",
				DisplayName = "Fenris Blessing",
				Flavour = Flavour.Kind,
				DefaultWeight = 0,
				Description = "The fenris armour set bonus, callable directly: faster on foot for five minutes. Not rolled on its own -- armour fitting dishes the sets out at random instead.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { ArmorSetEffects[0] }, 300f);
					return "The wolf-god lends his legs.";
				}
			},
			new RouletteEvent
			{
				Id = "berserk_fury",
				DisplayName = "Berserk",
				Flavour = Flavour.Kind,
				DefaultWeight = 0,
				Description = "The berserker armour set bonus, callable directly: hits land harder for 45 seconds. Not rolled on its own -- armour fitting dishes the sets out at random instead.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { ArmorSetEffects[1] }, 45f);
					return "The rage of the berserker takes hold of everyone.";
				}
			},
			new RouletteEvent
			{
				Id = "wolf_set",
				DisplayName = "Wolf Set",
				Flavour = Flavour.Kind,
				DefaultWeight = 0,
				Description = "The wolf armour set bonus, callable directly: frost resistance and truer blades for five minutes. Not rolled on its own.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { ArmorSetEffects[2] }, 300f);
					return "The pack's blessing settles on everyone.";
				}
			},
			new RouletteEvent
			{
				Id = "root_set",
				DisplayName = "Root Set",
				Flavour = Flavour.Kind,
				DefaultWeight = 0,
				Description = "The root armour set bonus, callable directly: arrows and poison slide off for five minutes. Not rolled on its own.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { ArmorSetEffects[3] }, 300f);
					return "Wooden ribs close over every chest.";
				}
			},
			new RouletteEvent
			{
				Id = "troll_set",
				DisplayName = "Troll Set",
				Flavour = Flavour.Kind,
				DefaultWeight = 0,
				Description = "The troll armour set bonus, callable directly: sneaking sharpens for five minutes. Not rolled on its own.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { ArmorSetEffects[4] }, 300f);
					return "Stones barely shift under anyone's feet.";
				}
			},
			new RouletteEvent
			{
				Id = "lox_set",
				DisplayName = "Lox Set",
				Flavour = Flavour.Kind,
				DefaultWeight = 0,
				Description = "The lox armour set bonus, callable directly: a lox's hide, blows land softer for five minutes. Not rolled on its own.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { ArmorSetEffects[5] }, 300f);
					return "Thick hides settle over everyone.";
				}
			},
			new RouletteEvent
			{
				Id = "harvester_set",
				DisplayName = "Harvester Set",
				Flavour = Flavour.Kind,
				DefaultWeight = 0,
				Description = "The harvester armour set bonus, callable directly: gathering comes swifter for five minutes. Not rolled on its own.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { ArmorSetEffects[6] }, 300f);
					return "Scythes swing faster in every hand.";
				}
			},
			new RouletteEvent
			{
				Id = "hold_lessons",
				DisplayName = "Lessons Held",
				Flavour = Flavour.Kind,
				DefaultWeight = 3,
				Description = "No skill drain for ten minutes. Applied once: the buff's duration lives in the client's status-effect asset, so a single application already lasts the whole ten minutes.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					ServerActions.AddStatusEffectToAll("SoftDeath");
					return "The gods hold your lessons this time.";
				}
			},
			new RouletteEvent
			{
				Id = "giants_strength",
				DisplayName = "Giant's Strength",
				Flavour = Flavour.Kind,
				DefaultWeight = 3,
				Description = "Megingjord for everyone for five minutes: +150 carry weight.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { "BeltStrength" }, 300f);
					return "Everyone shoulders a giant's burden.";
				}
			},
			new RouletteEvent
			{
				Id = "witches_brews",
				DisplayName = "Witch's Brews",
				Flavour = Flavour.Kind,
				DefaultWeight = 3,
				Description = "A potion for everyone, picked from every potion in the mod -- resistance brews, lightfoot mead, eitr mead, troll pheromones or the stamina mead. Applied once, and each player gets one they were not just handed last time, so a second roll points at a drink nobody is still carrying.",
				Fire = delegate
				{
					//IL_001e: Unknown result type (might be due to invalid IL or missing references)
					//IL_0023: Unknown result type (might be due to invalid IL or missing references)
					//IL_0030: Unknown result type (might be due to invalid IL or missing references)
					//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
					//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
					//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = ServerActions.OnlinePlayers();
					if (list.Count == 0)
					{
						return (string)null;
					}
					foreach (PlayerInfo item16 in list)
					{
						string last = (_lastPotionPerPlayer.TryGetValue(item16.m_name, out last) ? last : null);
						string text = ((last == null) ? Potions[Rng.Next(Potions.Length)] : Potions.Where((string b) => b != last).ToArray()[Rng.Next(Potions.Length - 1)]);
						_lastPotionPerPlayer[item16.m_name] = text;
						ServerActions.AddStatusEffect(item16.m_characterID, text);
					}
					return "A witch's brew is shared with all.";
				}
			},
			new RouletteEvent
			{
				Id = "wolf_sight",
				DisplayName = "Wolf Sight",
				Flavour = Flavour.Kind,
				DefaultWeight = 2,
				Description = "Wolf Sight on everyone for five minutes: hits land harder.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { "TrinketSilverDamage" }, 300f);
					return "Eyes sharpen. Hands steady.";
				}
			},
			new RouletteEvent
			{
				Id = "seasick",
				DisplayName = "Seasick",
				Flavour = Flavour.Mean,
				DefaultWeight = 3,
				Description = "Puke on everyone for 20 seconds: food breaks free.",
				Fire = delegate
				{
					ServerActions.AddStatusEffectToAll("Puke");
					return "The sea rolls in everyone's stomach.";
				}
			},
			new RouletteEvent
			{
				Id = "rooted",
				DisplayName = "Rooted",
				Flavour = Flavour.Mean,
				DefaultWeight = 3,
				Description = "Immobilized on everyone for 12 seconds: rooted in place.",
				Fire = delegate
				{
					ServerActions.AddStatusEffectToAll("Immobilized");
					return "The ground remembers how to hold.";
				}
			},
			new RouletteEvent
			{
				Id = "buried_luck",
				DisplayName = "Buried Luck",
				Flavour = Flavour.Harmless,
				DefaultWeight = 3,
				Description = "Wishbone on everyone until it is toggled off or they die: buried riches sing. The buff has no duration, so it stays until death.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					ServerActions.AddStatusEffectToAll("Wishbone");
					return "The earth hums with unclaimed treasure.";
				}
			},
			new RouletteEvent
			{
				Id = "dvergr_ally",
				DisplayName = "Dvergr Favour",
				Flavour = Flavour.Harmless,
				DefaultWeight = 2,
				Description = "Dvergr power on everyone for 90 seconds: a strange, brief glow.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { "SE_Dvergr_buff" }, 90f);
					return "A dwarf somewhere approves.";
				}
			},
			new RouletteEvent
			{
				Id = "lightfoot",
				DisplayName = "Lightfoot",
				Flavour = Flavour.Kind,
				DefaultWeight = 0,
				Description = "Lightfoot mead on everyone, handed out once and left to run its full ten minutes: lighter steps, no fall damage. Callable directly -- not rolled on its own, witch's brews dishes potions out at random instead.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					ServerActions.AddStatusEffectToAll("Potion_LightFoot");
					return "Everyone's steps grow light as a feather's.";
				}
			},
			new RouletteEvent
			{
				Id = "mage_luck",
				DisplayName = "Mage's Luck",
				Flavour = Flavour.Kind,
				DefaultWeight = 0,
				Description = "Minor eitr mead on everyone for three minutes: eitr regens. Callable directly -- not rolled on its own, witch's brews dishes potions out at random instead.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { "Potion_eitr_minor" }, 180f);
					return "Eitr flows freely for a little while.";
				}
			},
			new RouletteEvent
			{
				Id = "set_bonus",
				DisplayName = "Armour Fitting",
				Flavour = Flavour.Kind,
				DefaultWeight = 3,
				Description = "Gives everyone one of the separate armour-set bonuses at random: fenris, berserk, wolf, root, troll, lox or harvester, for five minutes (45 seconds for the berserker set).",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					string text = ArmorSetEffects[Rng.Next(ArmorSetEffects.Length)];
					EffectSustain.Run(new string[1] { text }, (text == ArmorSetEffects[1]) ? 45f : 300f);
					return "A suit of invisible armour settles on everyone: " + text + ".";
				}
			},
			new RouletteEvent
			{
				Id = "troll_love",
				DisplayName = "Troll Love",
				Flavour = Flavour.Mean,
				DefaultWeight = 0,
				Description = "Troll pheromones on everyone for two minutes: trolls warm to people. Callable directly -- not rolled on its own, witch's brews dishes potions out at random instead.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					EffectSustain.Run(new string[1] { "Potion_TrollPheromones" }, 120f);
					return "Everyone suddenly smells irresistible.";
				}
			},
			new RouletteEvent
			{
				Id = "fishing_knot",
				DisplayName = "Angler's Luck",
				Flavour = Flavour.Harmless,
				DefaultWeight = 2,
				Description = "Fishing hat set bonus on everyone until it is toggled off or they die: pure luck and triumph. The buff has no duration, so it stays until death.",
				Fire = delegate
				{
					if (ServerActions.OnlinePlayers().Count == 0)
					{
						return (string)null;
					}
					ServerActions.AddStatusEffectToAll("SetEffect_FishingHat");
					return "Every fishing line in the world pulls taut.";
				}
			},
			new RouletteEvent
			{
				Id = "magic_barrier",
				DisplayName = "Magic Barrier",
				Flavour = Flavour.Kind,
				DefaultWeight = 2,
				Description = "The staff of protection's barrier wraps everyone.",
				Fire = delegate
				{
					ServerActions.AddStatusEffectToAll("Staff_shield");
					return "A shimmering bubble seals itself around everyone.";
				}
			},
			new RouletteEvent
			{
				Id = "shaman_shield",
				DisplayName = "Shaman's Ward",
				Flavour = Flavour.Kind,
				DefaultWeight = 2,
				Description = "The fuling shaman's ward on everyone.",
				Fire = delegate
				{
					ServerActions.AddStatusEffectToAll("GoblinShaman_shield");
					return "A woodland chant swells around everyone.";
				}
			},
			new RouletteEvent
			{
				Id = "maddened",
				DisplayName = "Maddened Beasts",
				Flavour = Flavour.Mean,
				DefaultWeight = 3,
				Description = "Neutral and tamed creatures near every player turn hostile for a minute, the same move the aggravate console command makes; they calm down again after 60 s.",
				Fire = delegate
				{
					//IL_0021: Unknown result type (might be due to invalid IL or missing references)
					//IL_0026: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = ServerActions.OnlinePlayers();
					if (list.Count == 0)
					{
						return (string)null;
					}
					List<BaseAI> list2 = new List<BaseAI>();
					foreach (PlayerInfo item17 in list)
					{
						ServerActions.AggravateNeutrals(ServerActions.PositionOf(item17), 20f, list2);
					}
					if (list2.Count > 0)
					{
						List<BaseAI> calm = list2;
						EffectSustain.Delay(60f, delegate
						{
							ServerActions.CalmCreatures(calm);
						});
					}
					return "Something upsets the beasts of the forest.";
				}
			},
			new RouletteEvent
			{
				Id = "animal_friends",
				DisplayName = "Kindred Spirits",
				Flavour = Flavour.Kind,
				DefaultWeight = 2,
				Description = "A handful of wild animals (boars, wolves, lox, hens) near every player become tamed on the spot, ears perk up and tails wag.",
				Fire = delegate
				{
					//IL_001d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0022: Unknown result type (might be due to invalid IL or missing references)
					//IL_0024: Unknown result type (might be due to invalid IL or missing references)
					//IL_0025: Unknown result type (might be due to invalid IL or missing references)
					List<PlayerInfo> list = ServerActions.OnlinePlayers();
					if (list.Count == 0)
					{
						return (string)null;
					}
					int num = 0;
					foreach (PlayerInfo item18 in list)
					{
						num += ServerActions.TameNearby(ServerActions.PositionOf(item18), 30f, 4);
					}
					return num switch
					{
						0 => null, 
						1 => "The animals warm to their new friend.", 
						_ => "The animals warm to their new friends.", 
					};
				}
			}
		};

		private static List<string> RaidPool => (from s in (_raidPool?.Value ?? "").Split(',')
			select s.Trim() into s
			where s.Length > 0
			select s).ToList();

		internal static string ThunderFx => _thunderFx?.Value ?? "fx_redlightning_burst+fx_chainlightning_hit+fx_lightningweapon_hit|fx_JotunWitch_LightningBolt_Explosion+fx_himminafl_aoe";

		private static (string Location, string Label, PinType Pin, int Weight) WeightedLandmark((string Location, string Label, PinType Pin, int Weight)[] candidates)
		{
			int num = candidates.Sum(((string Location, string Label, PinType Pin, int Weight) c) => Math.Max(0, c.Weight));
			if (num <= 0)
			{
				return candidates[0];
			}
			int num2 = Rng.Next(num);
			for (int num3 = 0; num3 < candidates.Length; num3++)
			{
				(string, string, PinType, int) result = candidates[num3];
				num2 -= Math.Max(0, result.Item4);
				if (num2 < 0)
				{
					return result;
				}
			}
			return candidates[^1];
		}

		private static string FireRaid(DeathContext ctx)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			RandEventSystem res = RandEventSystem.instance;
			if ((Object)(object)res == (Object)null)
			{
				return null;
			}
			List<string> pool = RaidPool;
			if (_gateRaids == null || _gateRaids.Value)
			{
				List<KeyValuePair<RandomEvent, Vector3>> list = ServerActions.PossibleRaids();
				if (pool.Count > 0)
				{
					list = list.Where((KeyValuePair<RandomEvent, Vector3> kv) => pool.Contains(kv.Key.m_name)).ToList();
				}
				if (list.Count == 0)
				{
					if (Plugin.VerboseLogging.Value)
					{
						Plugin.Log.LogInfo((object)"No raid is currently valid for anyone online.");
					}
					return null;
				}
				KeyValuePair<RandomEvent, Vector3> keyValuePair = list[Rng.Next(list.Count)];
				if (!ServerActions.StartEvent(keyValuePair.Key.m_name, keyValuePair.Value))
				{
					return null;
				}
				return "The noise drew something: " + keyValuePair.Key.m_name + ".";
			}
			List<string> list2 = ((pool.Count > 0) ? pool : UngatedFallback.ToList()).Where((string n) => res.HaveEvent(n)).ToList();
			if (list2.Count == 0)
			{
				return null;
			}
			string text = list2[Rng.Next(list2.Count)];
			if (!ServerActions.StartEvent(text, ctx.Position))
			{
				return null;
			}
			return "The noise drew something: " + text + ".";
		}

		public static void BindConfig(ConfigFile config)
		{
			_gateRaids = config.Bind<bool>("Raids", "UseVanillaGating", true, "Only start raids vanilla considers valid right now, at a position it picks. That respects progression (global keys), biome and base checks, so an early base cannot be handed a late-game raid. Set false to force one from Pool at the corpse regardless of progress.");
			_thunderFx = config.Bind<string>("Effects", "Thunderclap", "fx_redlightning_burst+fx_chainlightning_hit+fx_lightningweapon_hit|fx_JotunWitch_LightningBolt_Explosion+fx_himminafl_aoe", "Effects for the thunderclap event. Variants separated by '|', prefabs within a variant by '+'. One variant is picked at random and all its prefabs play together at the same spot. Audition any of them live with: server dr fx <name> [more names...] -- reference/effect-prefab-names.txt lists all 1892. Note SpawnObject does a bare Instantiate with no setup, so a prefab whose visual needs runtime configuration (line-renderer endpoints, an Aoe or Projectile owner) will spawn and sound right while rendering wrong; fx_Lightning is exactly that.");
			_omenFx = config.Bind<string>("Effects", "AncientOmen", "sfx_frostfoundry_activate", "Effects for the ancient omen event, played at every player. Variants separated by '|', prefabs within a variant by '+'. One variant is picked at random and all its prefabs play together at the same spot. Audition any of them live with: server dr fx <name> [more names...] -- reference/effect-prefab-names.txt lists all 1892. Note SpawnObject does a bare Instantiate with no setup, so a prefab whose visual needs runtime configuration (line-renderer endpoints, an Aoe or Projectile owner) will spawn and sound right while rendering wrong; fx_Lightning is exactly that.");
			_lamentFx = config.Bind<string>("Effects", "Lament", "sfx_gjall_alerted+sfx_gjall_idle_vocals", "Effects for the lament event, played at every player. Variants separated by '|', prefabs within a variant by '+'. One variant is picked at random and all its prefabs play together at the same spot. Audition any of them live with: server dr fx <name> [more names...] -- reference/effect-prefab-names.txt lists all 1892. Note SpawnObject does a bare Instantiate with no setup, so a prefab whose visual needs runtime configuration (line-renderer endpoints, an Aoe or Projectile owner) will spawn and sound right while rendering wrong; fx_Lightning is exactly that.");
			_summonBoss = config.Bind<bool>("Events", "PilgrimageSummonsBoss", true, "Whether the pilgrimage event also respawns the boss at its altar. This can very easily wipe everyone online and leave the gear at an altar far from home. Set false to keep the mass teleport without the boss.");
			_thunderDamage = config.Bind<float>("Effects", "ThunderclapDamage", 10f, "Lightning damage the thunderclap deals to each player. Capped at a quarter of current health, so it can never be the killing blow. 0 disables the damage.");
			_adrenalineBuff = config.Bind<string>("Effects", "AdrenalineBuff", "Potion_stamina_medium", "Status effect the adrenaline event grants for its stamina-regen buff. Must be a vanilla status effect name resolvable by the client's ObjectDB. Note builds after 1.0.7 rename the stamina mead buff to Potion_stamina_medium; the old StaminaMedium name no longer exists and silently no-ops.");
			_loudGriefDuration = config.Bind<float>("Events", "LoudGriefDuration", 60f, "How long the loud grief event keeps everyone loud, in seconds. 0 disables the noise entirely (leaving only the optional spawns).");
			_loudGriefPower = config.Bind<float>("Events", "LoudGriefNoisePower", 40f, "How loud everyone is. Raised above creature alert ranges it lets creatures hear players through walls and around corners, and the same radius around each player is scanned every few seconds to wake and aggro every live creature there (tamed survivors are spared), so they stop idling and come. 10 is a whisper, 100 is a war horn.");
			_loudGriefSpawns = config.Bind<int>("Events", "LoudGriefSpawnCount", 2, "How many extra creatures LoudGriefSpawnPrefab spawns next to every online player while the grief carries. 0 disables outright spawns -- the sound alone is then just an attractor, not a guaranteed fight.");
			_loudGriefSpawnPrefab = config.Bind<string>("Events", "LoudGriefSpawnPrefab", "Greyling", "Creature spawn noise draws to every player. Any networked prefab is accepted, so late-game worlds can summon something that actually hurts.");
			_raidPool = config.Bind<string>("Raids", "Pool", "", "Comma-separated raid event names the 'raid' event may pick from. Unknown or disabled names are skipped. Leave EMPTY (the default) to allow whatever vanilla currently permits, which is usually what you want with UseVanillaGating on. Boss summons (boss_*, hildirboss1..3) are never offered: vanilla marks them m_random = false and the gating honours that.");
			foreach (RouletteEvent item in Table)
			{
				item.WeightConfig = config.Bind<int>("Weights", item.Id, item.DefaultWeight, $"[{item.Flavour}] {item.Description} Relative weight; 0 disables it.");
			}
		}

		public static RouletteEvent Find(string id)
		{
			return Table.FirstOrDefault((RouletteEvent e) => string.Equals(e.Id, id, StringComparison.OrdinalIgnoreCase));
		}

		public static void RollAndFire(DeathContext ctx)
		{
			List<RouletteEvent> list = Table.Where((RouletteEvent e) => e.Weight > 0).ToList();
			if (_lastFiredId != null && list.Count > 1)
			{
				list.RemoveAll((RouletteEvent e) => e.Id == _lastFiredId);
			}
			bool flag = Plugin.AnnounceEffects.Value && Plugin.CountdownSeconds.Value > 0f;
			while (list.Count > 0)
			{
				RouletteEvent pick = WeightedPick(list);
				if (pick == null)
				{
					break;
				}
				if (flag)
				{
					List<RouletteEvent> remaining = list;
					EffectSustain.Countdown(Plugin.CountdownSeconds.Value, delegate
					{
						if (!FireAndAnnounce(pick, ctx))
						{
							remaining.Remove(pick);
							FireRemaining(remaining, ctx);
						}
					});
					return;
				}
				if (FireAndAnnounce(pick, ctx))
				{
					return;
				}
				list.Remove(pick);
			}
			Plugin.Log.LogWarning((object)"No event could fire.");
		}

		private static void FireRemaining(List<RouletteEvent> candidates, DeathContext ctx)
		{
			while (candidates.Count > 0)
			{
				RouletteEvent rouletteEvent = WeightedPick(candidates);
				if (rouletteEvent == null)
				{
					break;
				}
				if (FireAndAnnounce(rouletteEvent, ctx))
				{
					return;
				}
				candidates.Remove(rouletteEvent);
			}
			Plugin.Log.LogWarning((object)"No event could fire after the countdown.");
		}

		public static bool FireAndAnnounce(RouletteEvent ev, DeathContext ctx)
		{
			string text;
			try
			{
				text = ev.Fire(ctx);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Event '{ev.Id}' threw: {arg}");
				return false;
			}
			if (text == null)
			{
				if (Plugin.VerboseLogging.Value)
				{
					Plugin.Log.LogInfo((object)("Event '" + ev.Id + "' declined."));
				}
				return false;
			}
			Plugin.Log.LogInfo((object)(ctx.PlayerName + " died -> '" + ev.Id + "': " + text));
			_lastFiredId = ev.Id;
			Announce.Effect(ctx, ev, text);
			return true;
		}

		private static RouletteEvent WeightedPick(List<RouletteEvent> pool)
		{
			int num = pool.Sum((RouletteEvent e) => Math.Max(0, e.Weight));
			if (num <= 0)
			{
				return null;
			}
			int num2 = Rng.Next(num);
			foreach (RouletteEvent item in pool)
			{
				num2 -= Math.Max(0, item.Weight);
				if (num2 < 0)
				{
					return item;
				}
			}
			return pool[pool.Count - 1];
		}

		private static List<PlayerInfo> Survivors(DeathContext ctx)
		{
			List<PlayerInfo> list = ServerActions.OnlinePlayers();
			if (ctx.IsTest)
			{
				return list;
			}
			return list.Where((PlayerInfo p) => p.m_characterID != ctx.CharacterId).ToList();
		}

		private static Vector3 RandomHorizontal()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			float num = (float)(Rng.NextDouble() * Math.PI * 2.0);
			return new Vector3(Mathf.Cos(num), 0f, Mathf.Sin(num));
		}

		public static void DumpWorldData()
		{
			if (_dumped)
			{
				return;
			}
			_dumped = true;
			RandEventSystem res = RandEventSystem.instance;
			if (res?.m_events != null)
			{
				Plugin.Log.LogInfo((object)("Random events in this world: " + string.Join(", ", res.m_events.Select((RandomEvent e) => e.m_name + (e.m_enabled ? "" : " (disabled)")))));
				List<string> list = RaidPool.Where((string n) => !res.HaveEvent(n)).ToList();
				if (list.Count > 0)
				{
					Plugin.Log.LogWarning((object)("Raid pool names not available here (will be skipped): " + string.Join(", ", list)));
				}
			}
			else
			{
				Plugin.Log.LogWarning((object)"RandEventSystem not available; raid names unverified.");
			}
			int num = Table.Count((RouletteEvent e) => e.Weight > 0);
			Plugin.Log.LogInfo((object)$"{num}/{Table.Count} events enabled.");
		}

		public static void EnsureDumped()
		{
			if (!_dumped)
			{
				DumpWorldData();
			}
		}
	}
	internal static class ServerActions
	{
		internal enum Element
		{
			Fire,
			Frost,
			Poison,
			Lightning,
			Spirit
		}

		private static MethodInfo _getPossible;

		public static void ShowMessage(MessageType type, string text)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected I4, but got Unknown
			if (!string.IsNullOrEmpty(text))
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ShowMessage", new object[2]
				{
					(int)type,
					text
				});
			}
		}

		public static void ShowMessageTo(long peerId, MessageType type, string text)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected I4, but got Unknown
			if (!string.IsNullOrEmpty(text) && peerId != 0L)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "ShowMessage", new object[2]
				{
					(int)type,
					text
				});
			}
		}

		public static void PlayEffect(string prefabName, Vector3 pos)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "SpawnObject", new object[3]
			{
				pos,
				Quaternion.identity,
				StringExtensionMethods.GetStableHashCode(prefabName)
			});
		}

		public static void PlayEffectAtEveryone(string prefabName)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			foreach (PlayerInfo item in OnlinePlayers())
			{
				PlayEffect(prefabName, PositionOf(item));
			}
		}

		public static bool SpawnNetworked(string prefabName, Vector3 pos)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			long num = AnyReadyPeer();
			if (num == 0L)
			{
				return false;
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(num, "SpawnObject", new object[3]
			{
				pos,
				Quaternion.identity,
				StringExtensionMethods.GetStableHashCode(prefabName)
			});
			return true;
		}

		public static void AddStatusEffect(ZDOID target, string statusEffectName, bool resetTime = true)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, target, "RPC_AddStatusEffect", new object[5]
			{
				StringExtensionMethods.GetStableHashCode(statusEffectName),
				resetTime,
				0,
				0f,
				-1
			});
		}

		public static void AddStatusEffectToAll(string statusEffectName)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			foreach (PlayerInfo item in OnlinePlayers())
			{
				AddStatusEffect(item.m_characterID, statusEffectName);
			}
		}

		public static void Heal(ZDOID target, float hp, bool showText = true)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, target, "RPC_Heal", new object[2] { hp, showText });
		}

		public static void DamageCapped(ZDOID target, float amount, float maxFractionOfHealth = 0.25f)
		{
			//IL_000c: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			ZDOMan instance = ZDOMan.instance;
			ZDO val = ((instance != null) ? instance.GetZDO(target) : null);
			if (val == null)
			{
				return;
			}
			float num = val.GetFloat(ZDOVars.s_health, 0f);
			if (!(num <= 1f))
			{
				float num2 = Mathf.Min(amount, num * maxFractionOfHealth);
				if (!(num2 <= 0f))
				{
					HitData val2 = new HitData(num2)
					{
						m_point = val.GetPosition()
					};
					ZRoutedRpc.instance.InvokeRoutedRPC(0L, target, "RPC_Damage", new object[1] { val2 });
				}
			}
		}

		public static void DamageElemental(ZDOID target, Element element, float amount, float maxFraction = 0.25f)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing referenc