Decompiled source of HorrorDirectorMod v0.1.3

BepInEx/plugins/HorrorDirectorMod/HorrorDirectorMod.dll

Decompiled 42 minutes 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 System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Dissonance;
using GameNetcodeStuff;
using HarmonyLib;
using HorrorDirectorMod.Anomaly;
using HorrorDirectorMod.Bootstrap;
using HorrorDirectorMod.Content;
using HorrorDirectorMod.Dev;
using HorrorDirectorMod.Diagnostics;
using HorrorDirectorMod.Director;
using HorrorDirectorMod.Input;
using HorrorDirectorMod.Lifecycle;
using HorrorDirectorMod.Objectives;
using HorrorDirectorMod.Observation;
using HorrorDirectorMod.Progression;
using HorrorDirectorMod.Terminal;
using Microsoft.CodeAnalysis;
using TerminalApi;
using TerminalApi.Classes;
using TerminalApi.Events;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

[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("HorrorDirectorMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("First feasibility probe for a Lethal Company horror director mod.")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: AssemblyInformationalVersion("0.1.3+032cd2883379c17aa34138bb9e6452b21b785dc2")]
[assembly: AssemblyProduct("HorrorDirectorMod")]
[assembly: AssemblyTitle("HorrorDirectorMod")]
[assembly: AssemblyVersion("0.1.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 HorrorDirectorMod
{
	[BepInPlugin("linar.horrordirector", "Horror Director Mod", "0.1.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "linar.horrordirector";

		public const string PluginName = "Horror Director Mod";

		public const string PluginVersion = "0.1.3";

		private Harmony? _harmony;

		internal static ManualLogSource ModLogger { get; private set; }

		private void Awake()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			ModLogger = ((BaseUnityPlugin)this).Logger;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			ConfigService config = new ConfigService(((BaseUnityPlugin)this).Config);
			ModServices.Initialize(((BaseUnityPlugin)this).Logger, config);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[HDM:Bootstrap] Horror Director Mod 0.1.3 loaded.");
			_harmony = new Harmony("linar.horrordirector");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[HDM:Bootstrap] Harmony patches applied.");
			ModServices.Diagnostics.StartBackgroundHeartbeat();
			ModServices.Diagnostics.LogHeartbeat("Awake smoke test");
		}

		private void OnEnable()
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[HDM:Bootstrap] Plugin enabled.");
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[HDM:Bootstrap] Plugin object destroyed; static services may continue for diagnostics.");
		}

		private void OnApplicationQuit()
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[HDM:Bootstrap] Application quitting.");
			ModServices.Shutdown();
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "HorrorDirectorMod";

		public const string PLUGIN_NAME = "HorrorDirectorMod";

		public const string PLUGIN_VERSION = "0.1.3";
	}
}
namespace HorrorDirectorMod.Terminal
{
	internal sealed class HauntedCaseTerminalService
	{
		private const string CommandWord = "hdmcase";

		private const string AliasCommandWord = "case";

		private readonly ManualLogSource _logger;

		private readonly ConfigService _config;

		private readonly WrongPlaceEpisodeDirector _wrongPlaceEpisodeDirector;

		private bool _registered;

		public HauntedCaseTerminalService(ManualLogSource logger, ConfigService config, WrongPlaceEpisodeDirector wrongPlaceEpisodeDirector)
		{
			_logger = logger;
			_config = config;
			_wrongPlaceEpisodeDirector = wrongPlaceEpisodeDirector;
		}

		public void Initialize()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			if (!_registered)
			{
				TerminalApi.AddCommand("hdmcase", new CommandInfo
				{
					Title = "HDM Case",
					Category = "Other",
					Description = "Displays the Horror Director case file.",
					DisplayTextSupplier = DisplayCaseFile
				}, (string)null, true);
				TerminalApi.AddCommand("case", new CommandInfo
				{
					Title = "Case",
					Category = "Other",
					Description = "Alias for the Horror Director case file.",
					DisplayTextSupplier = DisplayCaseFile
				}, (string)null, true);
				_registered = true;
				_logger.LogInfo((object)"[HDM:Terminal] Registered hdmcase/case commands.");
			}
		}

		public void Shutdown()
		{
			_registered = false;
		}

		private string DisplayCaseFile()
		{
			if (!_config.WrongPlaceAllowTerminalWrongness)
			{
				return "HDM CASE FILE\n\nTerminal wrongness is disabled by config.\n\n";
			}
			return "HDM CASE FILE\n\n" + $"State: {_wrongPlaceEpisodeDirector.State}\n" + "Episode: " + FormatEpisodeId() + "\nSignal: " + _wrongPlaceEpisodeDirector.TerminalStatus + "\n\nNotes:\n- ship safety signal unreliable\n- facility return route under observation\n- do not trust repeated sounds\n\n";
		}

		private string FormatEpisodeId()
		{
			if (!(_wrongPlaceEpisodeDirector.EpisodeId == Guid.Empty))
			{
				return _wrongPlaceEpisodeDirector.EpisodeId.ToString("N").Substring(0, 8);
			}
			return "none";
		}
	}
	internal sealed class TerminalAnomalyDebugService
	{
		private const string CommandWord = "hdmaudio";

		private const string AliasCommandWord = "audio";

		private readonly ManualLogSource _logger;

		private readonly GameObserver _observer;

		private readonly AnomalyEventBus _anomalyEventBus;

		private bool _registered;

		public TerminalAnomalyDebugService(ManualLogSource logger, GameObserver observer, AnomalyEventBus anomalyEventBus)
		{
			_logger = logger;
			_observer = observer;
			_anomalyEventBus = anomalyEventBus;
		}

		public void Initialize()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			if (!_registered)
			{
				TerminalApi.AddCommand("hdmaudio", new CommandInfo
				{
					Title = "HDM Audio",
					Category = "Other",
					Description = "Debug trigger for the Horror Director fake audio cue.",
					DisplayTextSupplier = TriggerFakeAudioCue
				}, (string)null, true);
				TerminalApi.AddCommand("audio", new CommandInfo
				{
					Title = "Audio",
					Category = "Other",
					Description = "Alias for the Horror Director fake audio cue debug trigger.",
					DisplayTextSupplier = TriggerFakeAudioCue
				}, (string)null, true);
				_registered = true;
				_logger.LogInfo((object)"[HDM:Terminal] Registered hdmaudio/audio commands.");
			}
		}

		public void Shutdown()
		{
			_registered = false;
		}

		private string TriggerFakeAudioCue()
		{
			StartOfRound round = Object.FindObjectOfType<StartOfRound>();
			GameObservation observation = _observer.Observe(round);
			AnomalyTriggerResult anomalyTriggerResult = _anomalyEventBus.TryTriggerDebugFakeAudioCue(observation, "terminal command hdmaudio/audio");
			if (anomalyTriggerResult.Success)
			{
				return "HDM AUDIO PROBE\n\nFake audio cue dispatched.\n" + $"Event id: {anomalyTriggerResult.EventId}\n\n";
			}
			return "HDM AUDIO PROBE\n\nFake audio cue was not dispatched.\nReason: " + anomalyTriggerResult.Message + "\n\n";
		}
	}
	internal sealed class TerminalFacilityObjectiveService
	{
		private const string SpawnCommandWord = "objspawn";

		private const string SpawnSelfCommandWord = "objself";

		private const string StartCommandWord = "objstart";

		private readonly ManualLogSource _logger;

		private readonly ConfigService _config;

		private readonly FacilityObjectiveService _objectives;

		private bool _registered;

		public TerminalFacilityObjectiveService(ManualLogSource logger, ConfigService config, FacilityObjectiveService objectives)
		{
			_logger = logger;
			_config = config;
			_objectives = objectives;
		}

		public void Initialize()
		{
			//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_003c: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0070: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Expected O, but got Unknown
			if (!_registered)
			{
				if (!_config.FacilityObjectiveEnabled)
				{
					_logger.LogInfo((object)"[HDM:Terminal] Facility objective commands disabled by config.");
					return;
				}
				TerminalApi.AddCommand("objspawn", new CommandInfo
				{
					Title = "HDM Objective Spawn",
					Category = "Other",
					Description = "Spawns a local facility objective debug panel near the player.",
					DisplayTextSupplier = SpawnObjective
				}, (string)null, true);
				TerminalApi.AddCommand("objself", new CommandInfo
				{
					Title = "HDM Objective Self",
					Category = "Other",
					Description = "Spawns a local facility objective debug panel at the player position.",
					DisplayTextSupplier = SpawnObjectiveAtPlayer
				}, (string)null, true);
				TerminalApi.AddCommand("objstart", new CommandInfo
				{
					Title = "HDM Objective Start",
					Category = "Other",
					Description = "Starts the pipe-routing facility objective probe.",
					DisplayTextSupplier = StartPipePuzzle
				}, (string)null, true);
				_registered = true;
				_logger.LogInfo((object)"[HDM:Terminal] Registered objspawn/objself/objstart commands.");
			}
		}

		public void Shutdown()
		{
			if (_registered)
			{
				_registered = false;
			}
		}

		private string SpawnObjective()
		{
			return FormatSpawnResult(_objectives.SpawnDebugObjective("terminal command objspawn"), "near you");
		}

		private string SpawnObjectiveAtPlayer()
		{
			return FormatSpawnResult(_objectives.SpawnDebugObjectiveAtPlayer("terminal command objself"), "at your position");
		}

		private string StartPipePuzzle()
		{
			return FormatPuzzleOpened(_objectives.StartPipePuzzleFromDebugCommand("terminal command objstart"));
		}

		private static string FormatSpawnResult(ObjectiveProbeResult result, string locationText)
		{
			if (result.Success)
			{
				return "HDM FACILITY OBJECTIVE\n\nObjective panel spawned " + locationText + ".\nObject: " + result.ObjectiveName + "\n\nLook at the panel and press E, or type objstart to open its local terminal.\n\n";
			}
			return "HDM FACILITY OBJECTIVE\n\nObjective panel was not spawned.\nReason: " + result.Message + "\n\n";
		}

		private static string FormatPuzzleOpened(PipePuzzle puzzle)
		{
			return "HDM FACILITY OBJECTIVE\n\nLocal objective terminal opened.\nUse the panel UI that appeared on your screen.\n\n" + $"Reward: {puzzle.RewardCredits} credits.\n\n";
		}
	}
	internal sealed class TerminalMathGameService
	{
		private sealed class MathPuzzle
		{
			public string Expression { get; }

			public int Answer { get; }

			public int RewardCredits { get; }

			public MathPuzzle(string expression, int answer, int rewardCredits)
			{
				Expression = expression;
				Answer = answer;
				RewardCredits = rewardCredits;
			}
		}

		private const string CommandWord = "math";

		private readonly ManualLogSource _logger;

		private readonly ConfigService _config;

		private readonly Random _random = new Random();

		private MathPuzzle? _pendingPuzzle;

		private bool _registered;

		public TerminalMathGameService(ManualLogSource logger, ConfigService config)
		{
			_logger = logger;
			_config = config;
		}

		public void Initialize()
		{
			//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_003c: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			if (!_registered)
			{
				if (!_config.TerminalMathEnabled)
				{
					_logger.LogInfo((object)"[HDM:Terminal] Math command disabled by config.");
					return;
				}
				TerminalApi.AddCommand("math", new CommandInfo
				{
					Title = "Math",
					Category = "Other",
					Description = "Solve a small arithmetic task for credits.",
					DisplayTextSupplier = StartPuzzle
				}, (string)null, true);
				Events.TerminalParsedSentence += new TerminalParseSentenceEventHandler(OnTerminalParsedSentence);
				_registered = true;
				_logger.LogInfo((object)"[HDM:Terminal] Registered math command.");
			}
		}

		public void Shutdown()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			if (_registered)
			{
				Events.TerminalParsedSentence -= new TerminalParseSentenceEventHandler(OnTerminalParsedSentence);
				_registered = false;
				_pendingPuzzle = null;
			}
		}

		private string StartPuzzle()
		{
			_pendingPuzzle = GeneratePuzzle();
			_logger.LogInfo((object)$"[HDM:Terminal] Math puzzle generated: expression='{_pendingPuzzle.Expression}', answer={_pendingPuzzle.Answer}, reward={_pendingPuzzle.RewardCredits}.");
			return FormatPuzzlePrompt(_pendingPuzzle);
		}

		private void OnTerminalParsedSentence(object sender, TerminalParseSentenceEventArgs e)
		{
			if (_pendingPuzzle == null || e.ReturnedNode == null)
			{
				return;
			}
			string text = (e.SubmittedText ?? string.Empty).Trim();
			if (!text.Equals("math", StringComparison.OrdinalIgnoreCase))
			{
				if (!int.TryParse(text, out var result))
				{
					e.ReturnedNode.displayText = FormatInvalidAnswer(_pendingPuzzle);
					e.ReturnedNode.clearPreviousText = true;
					return;
				}
				if (result != _pendingPuzzle.Answer)
				{
					_logger.LogInfo((object)$"[HDM:Terminal] Math puzzle answered incorrectly: submitted={result}, expected={_pendingPuzzle.Answer}.");
					e.ReturnedNode.displayText = FormatWrongAnswer(_pendingPuzzle);
					e.ReturnedNode.clearPreviousText = true;
					return;
				}
				int rewardCredits = _pendingPuzzle.RewardCredits;
				int num = AwardCredits(((TerminalEventArgs)e).Terminal, rewardCredits);
				_logger.LogInfo((object)$"[HDM:Terminal] Math puzzle solved. reward={rewardCredits}, credits={num}.");
				e.ReturnedNode.displayText = FormatSuccess(rewardCredits, num);
				e.ReturnedNode.clearPreviousText = true;
				_pendingPuzzle = null;
			}
		}

		private MathPuzzle GeneratePuzzle()
		{
			if (_random.Next(0, 2) == 0)
			{
				int num = _random.Next(2, 13);
				int num2 = _random.Next(2, 13);
				return new MathPuzzle($"{num} * {num2}", num * num2, _config.TerminalMathRewardCredits);
			}
			int num3 = _random.Next(1, 51);
			int num4 = _random.Next(1, 51);
			return new MathPuzzle($"{num3} + {num4}", num3 + num4, _config.TerminalMathRewardCredits);
		}

		private static int AwardCredits(Terminal terminal, int reward)
		{
			if (terminal == null || reward <= 0)
			{
				return terminal?.groupCredits ?? 0;
			}
			terminal.groupCredits += reward;
			return terminal.groupCredits;
		}

		private static string FormatPuzzlePrompt(MathPuzzle puzzle)
		{
			return "HORROR DIRECTOR MATH PROBE\n\nSolve: " + puzzle.Expression + " = ?\n\nType only the answer and press enter.\n" + $"Reward: {puzzle.RewardCredits} credits.\n\n";
		}

		private static string FormatInvalidAnswer(MathPuzzle puzzle)
		{
			return "Enter a number to answer the active math task.\n\nSolve: " + puzzle.Expression + " = ?\n\n";
		}

		private static string FormatWrongAnswer(MathPuzzle puzzle)
		{
			return "Incorrect. Try again.\n\nSolve: " + puzzle.Expression + " = ?\n\n";
		}

		private static string FormatSuccess(int reward, int credits)
		{
			return "Correct.\n\n" + $"Awarded {reward} credits.\n" + $"Current group credits: {credits}.\n\n" + "Type math for another task.\n\n";
		}
	}
	internal sealed class TerminalPropDebugService
	{
		private const string SpawnCommandWord = "propspawn";

		private const string SpawnSelfCommandWord = "propself";

		private const string AnomalyCommandWord = "propanomaly";

		private const string AnomalyAliasCommandWord = "propgo";

		private const string NearbyAnomalyCommandWord = "propnear";

		private readonly ManualLogSource _logger;

		private readonly PropAnomalyService _propAnomaly;

		private bool _registered;

		public TerminalPropDebugService(ManualLogSource logger, PropAnomalyService propAnomaly)
		{
			_logger = logger;
			_propAnomaly = propAnomaly;
		}

		public void Initialize()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Expected O, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Expected O, but got Unknown
			if (!_registered)
			{
				TerminalApi.AddCommand("propspawn", new CommandInfo
				{
					Title = "HDM Prop Spawn",
					Category = "Other",
					Description = "Spawns a local debug prop near the player.",
					DisplayTextSupplier = SpawnDebugProp
				}, (string)null, true);
				TerminalApi.AddCommand("propanomaly", new CommandInfo
				{
					Title = "HDM Prop Anomaly",
					Category = "Other",
					Description = "Triggers a local anomaly on the last debug prop.",
					DisplayTextSupplier = TriggerPropAnomaly
				}, (string)null, true);
				TerminalApi.AddCommand("propself", new CommandInfo
				{
					Title = "HDM Prop Self",
					Category = "Other",
					Description = "Spawns a local debug prop directly at the player position.",
					DisplayTextSupplier = SpawnDebugPropAtPlayer
				}, (string)null, true);
				TerminalApi.AddCommand("propnear", new CommandInfo
				{
					Title = "HDM Prop Near",
					Category = "Other",
					Description = "Triggers an anomaly on the nearest real grabbable item.",
					DisplayTextSupplier = TriggerNearestGrabbableAnomaly
				}, (string)null, true);
				TerminalApi.AddCommand("propgo", new CommandInfo
				{
					Title = "HDM Prop Go",
					Category = "Other",
					Description = "Alias for the debug prop anomaly.",
					DisplayTextSupplier = TriggerPropAnomaly
				}, (string)null, true);
				_registered = true;
				_logger.LogInfo((object)"[HDM:Terminal] Registered propspawn/propself/propanomaly/propgo/propnear commands.");
			}
		}

		public void Shutdown()
		{
			_registered = false;
		}

		private string SpawnDebugProp()
		{
			PropProbeResult propProbeResult = _propAnomaly.SpawnDebugProp("terminal command propspawn");
			if (propProbeResult.Success)
			{
				return "HDM PROP PROBE\n\nDebug prop spawned near you.\nProp: " + propProbeResult.PropName + "\n\nRun propanomaly or propgo to make it move.\n\n";
			}
			return "HDM PROP PROBE\n\nDebug prop was not spawned.\nReason: " + propProbeResult.Message + "\n\n";
		}

		private string SpawnDebugPropAtPlayer()
		{
			PropProbeResult propProbeResult = _propAnomaly.SpawnDebugPropAtPlayerPosition("terminal command propself");
			if (propProbeResult.Success)
			{
				return "HDM PROP PROBE\n\nDebug prop spawned at your position.\nProp: " + propProbeResult.PropName + "\n\nRun propanomaly or propgo to make it move.\n\n";
			}
			return "HDM PROP PROBE\n\nDebug prop was not spawned at your position.\nReason: " + propProbeResult.Message + "\n\n";
		}

		private string TriggerPropAnomaly()
		{
			PropProbeResult propProbeResult = _propAnomaly.TriggerDebugAnomaly("terminal command propanomaly/propgo");
			if (propProbeResult.Success)
			{
				return "HDM PROP PROBE\n\nProp anomaly triggered.\nProp: " + propProbeResult.PropName + "\n\n";
			}
			return "HDM PROP PROBE\n\nProp anomaly was not triggered.\nReason: " + propProbeResult.Message + "\n\n";
		}

		private string TriggerNearestGrabbableAnomaly()
		{
			PropProbeResult propProbeResult = _propAnomaly.TriggerNearestGrabbableAnomaly("terminal command propnear");
			if (propProbeResult.Success)
			{
				return "HDM PROP PROBE\n\nNearest real item anomaly triggered.\nItem: " + propProbeResult.PropName + "\n\n";
			}
			return "HDM PROP PROBE\n\nNearest real item anomaly was not triggered.\nReason: " + propProbeResult.Message + "\n\n";
		}
	}
}
namespace HorrorDirectorMod.Progression
{
	internal interface IModifierApplier
	{
		void Apply(PlayerControllerB player, ProgressionTotals totals, string reason);

		void Restore(PlayerControllerB? player, string reason);
	}
	internal sealed class PlayerStatApplier : IModifierApplier
	{
		private readonly struct PlayerBaseline
		{
			public ulong PlayerClientId { get; }

			public int Health { get; }

			public float MovementSpeed { get; }

			public float SprintTime { get; }

			public float GrabDistance { get; }

			public PlayerBaseline(ulong playerClientId, int health, float movementSpeed, float sprintTime, float grabDistance)
			{
				PlayerClientId = playerClientId;
				Health = health;
				MovementSpeed = movementSpeed;
				SprintTime = sprintTime;
				GrabDistance = grabDistance;
			}
		}

		private readonly ManualLogSource _logger;

		private PlayerBaseline? _baseline;

		private bool _hasAppliedTotals;

		public PlayerStatApplier(ManualLogSource logger)
		{
			_logger = logger;
		}

		public void Apply(PlayerControllerB player, ProgressionTotals totals, string reason)
		{
			EnsureBaseline(player);
			if (_baseline.HasValue)
			{
				PlayerBaseline value = _baseline.Value;
				int num = Math.Max(1, value.Health + totals.HealthBonus);
				float num2 = value.MovementSpeed * Math.Max(0.1f, totals.MovementMultiplier);
				float num3 = value.SprintTime * Math.Max(0.1f, totals.SprintTimeMultiplier);
				float num4 = value.GrabDistance * Math.Max(0.1f, totals.GrabDistanceMultiplier);
				player.movementSpeed = num2;
				player.sprintTime = num3;
				player.grabDistance = num4;
				if (!player.isPlayerDead && player.health < num)
				{
					player.health = num;
				}
				if (!_hasAppliedTotals || reason != "tick")
				{
					_logger.LogInfo((object)$"[HDM:Progression] Applied stats: player={player.playerClientId}, healthTarget={num}, move={num2:0.00}, sprintTime={num3:0.00}, grab={num4:0.00}, reason='{reason}'.");
					_hasAppliedTotals = true;
				}
			}
		}

		public void Restore(PlayerControllerB? player, string reason)
		{
			if (player == null || !_baseline.HasValue)
			{
				return;
			}
			PlayerBaseline value = _baseline.Value;
			if (value.PlayerClientId == player.playerClientId)
			{
				player.movementSpeed = value.MovementSpeed;
				player.sprintTime = value.SprintTime;
				player.grabDistance = value.GrabDistance;
				if (!player.isPlayerDead && player.health > value.Health)
				{
					player.health = value.Health;
				}
				_hasAppliedTotals = false;
				_logger.LogInfo((object)$"[HDM:Progression] Restored baseline stats: player={player.playerClientId}, reason='{reason}'.");
			}
		}

		public void ForgetPlayer()
		{
			_baseline = null;
			_hasAppliedTotals = false;
		}

		private void EnsureBaseline(PlayerControllerB player)
		{
			if (!_baseline.HasValue || _baseline.Value.PlayerClientId != player.playerClientId)
			{
				_baseline = new PlayerBaseline(player.playerClientId, Math.Max(1, player.health), Math.Max(0.1f, player.movementSpeed), Math.Max(0.1f, player.sprintTime), Math.Max(0.1f, player.grabDistance));
				_hasAppliedTotals = false;
				_logger.LogInfo((object)$"[HDM:Progression] Captured baseline stats: player={player.playerClientId}, health={player.health}, move={player.movementSpeed:0.00}, sprintTime={player.sprintTime:0.00}, grab={player.grabDistance:0.00}.");
			}
		}
	}
	internal enum ProgressionUpgradeId
	{
		None,
		ReinforcedSuit,
		EmergencyPadding,
		RunnerConditioning,
		BiggerLungs,
		SteadyHands,
		DeepPockets,
		AdrenalineStep,
		SteadyNerves,
		IronWill
	}
	internal enum ProgressionEffectKind
	{
		HealthBonus,
		MovementMultiplier,
		SprintTimeMultiplier,
		GrabDistanceMultiplier,
		Placeholder
	}
	internal sealed class UpgradeDefinition
	{
		public string Id { get; }

		public ProgressionUpgradeId NetworkId { get; }

		public string Branch { get; }

		public string Title { get; }

		public string Description { get; }

		public int Cost { get; }

		public ProgressionEffectKind EffectKind { get; }

		public float EffectValue { get; }

		public bool Available { get; }

		public UpgradeDefinition(string id, ProgressionUpgradeId networkId, string branch, string title, string description, int cost, ProgressionEffectKind effectKind, float effectValue, bool available)
		{
			Id = id;
			NetworkId = networkId;
			Branch = branch;
			Title = title;
			Description = description;
			Cost = cost;
			EffectKind = effectKind;
			EffectValue = effectValue;
			Available = available;
		}
	}
	internal enum ProgressionNetworkMessageKind
	{
		PurchaseRequest = 1,
		ResetRequest = 2,
		RequestState = 3,
		StateSnapshot = 10,
		PurchaseRejected = 11
	}
	internal enum ProgressionRejectReason
	{
		None,
		UnknownUpgrade,
		Unavailable,
		AlreadyOwned,
		NotEnoughPoints,
		NotHost,
		NetworkUnavailable
	}
	internal readonly struct ProgressionNetworkSnapshot
	{
		public ulong TargetClientId { get; }

		public int AvailablePoints { get; }

		public int OwnedMask { get; }

		public ProgressionNetworkSnapshot(ulong targetClientId, int availablePoints, int ownedMask)
		{
			TargetClientId = targetClientId;
			AvailablePoints = availablePoints;
			OwnedMask = ownedMask;
		}
	}
	internal readonly struct ProgressionNetworkReject
	{
		public ulong TargetClientId { get; }

		public ProgressionUpgradeId UpgradeId { get; }

		public ProgressionRejectReason Reason { get; }

		public ProgressionNetworkReject(ulong targetClientId, ProgressionUpgradeId upgradeId, ProgressionRejectReason reason)
		{
			TargetClientId = targetClientId;
			UpgradeId = upgradeId;
			Reason = reason;
		}
	}
	internal readonly struct ProgressionTotals
	{
		public int HealthBonus { get; }

		public float MovementMultiplier { get; }

		public float SprintTimeMultiplier { get; }

		public float GrabDistanceMultiplier { get; }

		public ProgressionTotals(int healthBonus, float movementMultiplier, float sprintTimeMultiplier, float grabDistanceMultiplier)
		{
			HealthBonus = healthBonus;
			MovementMultiplier = movementMultiplier;
			SprintTimeMultiplier = sprintTimeMultiplier;
			GrabDistanceMultiplier = grabDistanceMultiplier;
		}
	}
	internal readonly struct ProgressionSnapshot
	{
		public int AvailablePoints { get; }

		public int StartingPoints { get; }

		public bool IsRequestPending { get; }

		public string LastRejectedReason { get; }

		public ProgressionSnapshot(int availablePoints, int startingPoints, bool isRequestPending, string lastRejectedReason)
		{
			AvailablePoints = availablePoints;
			StartingPoints = startingPoints;
			IsRequestPending = isRequestPending;
			LastRejectedReason = lastRejectedReason;
		}
	}
	internal sealed class ProgressionPlayerState
	{
		private readonly HashSet<ProgressionUpgradeId> _ownedUpgradeIds = new HashSet<ProgressionUpgradeId>();

		public ulong ClientId { get; }

		public int AvailablePoints { get; private set; }

		public IEnumerable<ProgressionUpgradeId> OwnedUpgradeIds => _ownedUpgradeIds;

		public ProgressionPlayerState(ulong clientId, int availablePoints)
		{
			ClientId = clientId;
			AvailablePoints = availablePoints;
		}

		public bool IsOwned(ProgressionUpgradeId upgradeId)
		{
			return _ownedUpgradeIds.Contains(upgradeId);
		}

		public bool TryPurchase(ProgressionUpgradeId upgradeId, int cost)
		{
			if (_ownedUpgradeIds.Contains(upgradeId) || AvailablePoints < cost)
			{
				return false;
			}
			AvailablePoints -= cost;
			_ownedUpgradeIds.Add(upgradeId);
			return true;
		}

		public void Reset(int availablePoints)
		{
			AvailablePoints = availablePoints;
			_ownedUpgradeIds.Clear();
		}

		public int ToOwnedMask()
		{
			int num = 0;
			foreach (ProgressionUpgradeId ownedUpgradeId in _ownedUpgradeIds)
			{
				num |= 1 << (int)ownedUpgradeId;
			}
			return num;
		}

		public void ReplaceFromSnapshot(int availablePoints, int ownedMask)
		{
			AvailablePoints = availablePoints;
			_ownedUpgradeIds.Clear();
			for (int i = 1; i <= 9; i++)
			{
				if ((ownedMask & (1 << i)) != 0)
				{
					_ownedUpgradeIds.Add((ProgressionUpgradeId)i);
				}
			}
		}
	}
	internal sealed class ProgressionNetworkService
	{
		private const string MessageName = "HDM_Progression_v1";

		private const int Version = 1;

		private const int PayloadBytes = 48;

		private readonly ManualLogSource _logger;

		private Action<ulong, ProgressionNetworkMessageKind, ProgressionUpgradeId>? _onHostRequestReceived;

		private Action<ProgressionNetworkSnapshot>? _onSnapshotReceived;

		private Action<ProgressionNetworkReject>? _onRejectReceived;

		private CustomMessagingManager? _registeredMessagingManager;

		public string LastMessage { get; private set; }

		public bool IsListening
		{
			get
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton == null)
				{
					return false;
				}
				return singleton.IsListening;
			}
		}

		public bool IsHost
		{
			get
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton == null)
				{
					return false;
				}
				return singleton.IsHost;
			}
		}

		public bool IsServer
		{
			get
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton == null)
				{
					return false;
				}
				return singleton.IsServer;
			}
		}

		public ulong LocalClientId
		{
			get
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton == null)
				{
					return 0uL;
				}
				return singleton.LocalClientId;
			}
		}

		public ProgressionNetworkService(ManualLogSource logger)
		{
			_logger = logger;
			LastMessage = "none";
		}

		public void Initialize(Action<ulong, ProgressionNetworkMessageKind, ProgressionUpgradeId> onHostRequestReceived, Action<ProgressionNetworkSnapshot> onSnapshotReceived, Action<ProgressionNetworkReject> onRejectReceived)
		{
			_onHostRequestReceived = onHostRequestReceived;
			_onSnapshotReceived = onSnapshotReceived;
			_onRejectReceived = onRejectReceived;
			EnsureRegistered();
		}

		public void Tick()
		{
			EnsureRegistered();
		}

		public void Shutdown()
		{
			if (_registeredMessagingManager != null)
			{
				_registeredMessagingManager.UnregisterNamedMessageHandler("HDM_Progression_v1");
				_registeredMessagingManager = null;
			}
			_onHostRequestReceived = null;
			_onSnapshotReceived = null;
			_onRejectReceived = null;
		}

		public bool TrySendPurchaseRequest(ProgressionUpgradeId upgradeId)
		{
			return TrySendRequest(ProgressionNetworkMessageKind.PurchaseRequest, upgradeId);
		}

		public bool TrySendResetRequest()
		{
			return TrySendRequest(ProgressionNetworkMessageKind.ResetRequest, ProgressionUpgradeId.None);
		}

		public bool TrySendStateRequest()
		{
			return TrySendRequest(ProgressionNetworkMessageKind.RequestState, ProgressionUpgradeId.None);
		}

		public bool TrySendSnapshot(ulong targetClientId, ProgressionPlayerState state)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (((singleton != null) ? singleton.CustomMessagingManager : null) == null || !singleton.IsListening)
			{
				LastMessage = "snapshot blocked: network unavailable";
				return false;
			}
			if (targetClientId == singleton.LocalClientId)
			{
				return false;
			}
			EnsureRegistered();
			return TrySend(singleton, targetClientId, ProgressionNetworkMessageKind.StateSnapshot, ProgressionUpgradeId.None, state.ClientId, state.AvailablePoints, state.ToOwnedMask(), ProgressionRejectReason.None);
		}

		public bool TrySendReject(ulong targetClientId, ProgressionUpgradeId upgradeId, ProgressionRejectReason reason)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (((singleton != null) ? singleton.CustomMessagingManager : null) == null || !singleton.IsListening)
			{
				LastMessage = "reject blocked: network unavailable";
				return false;
			}
			if (targetClientId == singleton.LocalClientId)
			{
				return false;
			}
			EnsureRegistered();
			return TrySend(singleton, targetClientId, ProgressionNetworkMessageKind.PurchaseRejected, upgradeId, targetClientId, 0, 0, reason);
		}

		public string DescribeRole()
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (singleton == null)
			{
				return "network=null";
			}
			return $"listening={singleton.IsListening}, host={singleton.IsHost}, server={singleton.IsServer}, client={singleton.IsClient}, localClient={singleton.LocalClientId}";
		}

		private bool TrySendRequest(ProgressionNetworkMessageKind kind, ProgressionUpgradeId upgradeId)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if (((singleton != null) ? singleton.CustomMessagingManager : null) == null || !singleton.IsListening)
			{
				LastMessage = $"{kind} blocked: network unavailable";
				return false;
			}
			EnsureRegistered();
			return TrySend(singleton, 0uL, kind, upgradeId, singleton.LocalClientId, 0, 0, ProgressionRejectReason.None);
		}

		private unsafe bool TrySend(NetworkManager networkManager, ulong clientId, ProgressionNetworkMessageKind kind, ProgressionUpgradeId upgradeId, ulong targetClientId, int availablePoints, int ownedMask, ProgressionRejectReason rejectReason)
		{
			//IL_000b: 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)
			try
			{
				FastBufferWriter val = default(FastBufferWriter);
				((FastBufferWriter)(ref val))..ctor(48, (Allocator)2, -1);
				try
				{
					WritePayload(val, kind, upgradeId, targetClientId, availablePoints, ownedMask, rejectReason);
					networkManager.CustomMessagingManager.SendNamedMessage("HDM_Progression_v1", clientId, val, (NetworkDelivery)3);
					LastMessage = $"sent {kind}: to={clientId}, target={targetClientId}, upgrade={upgradeId}, points={availablePoints}, mask={ownedMask}, reason={rejectReason}";
					_logger.LogInfo((object)("[HDM:ProgressionNet] " + LastMessage + "."));
					return true;
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
				}
			}
			catch (Exception ex)
			{
				LastMessage = $"send failed {kind}: {ex.GetType().Name}: {ex.Message}";
				_logger.LogWarning((object)("[HDM:ProgressionNet] " + LastMessage));
				return false;
			}
		}

		private void EnsureRegistered()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null);
			if (val != null && val != _registeredMessagingManager)
			{
				if (_registeredMessagingManager != null)
				{
					_registeredMessagingManager.UnregisterNamedMessageHandler("HDM_Progression_v1");
				}
				val.RegisterNamedMessageHandler("HDM_Progression_v1", new HandleNamedMessageDelegate(OnNamedMessage));
				_registeredMessagingManager = val;
				_logger.LogInfo((object)"[HDM:ProgressionNet] Registered progression named message handler.");
			}
		}

		private void OnNamedMessage(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ReadPayload(reader, out var kind, out var upgradeId, out var targetClientId, out var availablePoints, out var ownedMask, out var rejectReason);
				LastMessage = $"received {kind}: sender={senderClientId}, target={targetClientId}, upgrade={upgradeId}, points={availablePoints}, mask={ownedMask}, reason={rejectReason}";
				_logger.LogInfo((object)("[HDM:ProgressionNet] " + LastMessage + "."));
				switch (kind)
				{
				case ProgressionNetworkMessageKind.PurchaseRequest:
				case ProgressionNetworkMessageKind.ResetRequest:
				case ProgressionNetworkMessageKind.RequestState:
					_onHostRequestReceived?.Invoke(senderClientId, kind, upgradeId);
					break;
				case ProgressionNetworkMessageKind.StateSnapshot:
					_onSnapshotReceived?.Invoke(new ProgressionNetworkSnapshot(targetClientId, availablePoints, ownedMask));
					break;
				case ProgressionNetworkMessageKind.PurchaseRejected:
					_onRejectReceived?.Invoke(new ProgressionNetworkReject(targetClientId, upgradeId, rejectReason));
					break;
				}
			}
			catch (Exception ex)
			{
				LastMessage = "receive failed: " + ex.GetType().Name + ": " + ex.Message;
				_logger.LogWarning((object)("[HDM:ProgressionNet] " + LastMessage));
			}
		}

		private static void WritePayload(FastBufferWriter writer, ProgressionNetworkMessageKind kind, ProgressionUpgradeId upgradeId, ulong targetClientId, int availablePoints, int ownedMask, ProgressionRejectReason rejectReason)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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_0048: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			int num = 1;
			((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref num, default(ForPrimitives));
			num = (int)kind;
			((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref num, default(ForPrimitives));
			num = (int)upgradeId;
			((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref num, default(ForPrimitives));
			((FastBufferWriter)(ref writer)).WriteValueSafe<ulong>(ref targetClientId, default(ForPrimitives));
			((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref availablePoints, default(ForPrimitives));
			((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref ownedMask, default(ForPrimitives));
			num = (int)rejectReason;
			((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref num, default(ForPrimitives));
		}

		private static void ReadPayload(FastBufferReader reader, out ProgressionNetworkMessageKind kind, out ProgressionUpgradeId upgradeId, out ulong targetClientId, out int availablePoints, out int ownedMask, out ProgressionRejectReason rejectReason)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0046: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			int num = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			if (num != 1)
			{
				throw new InvalidOperationException($"Unsupported progression payload version {num}.");
			}
			int num2 = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num2, default(ForPrimitives));
			int num3 = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num3, default(ForPrimitives));
			((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref targetClientId, default(ForPrimitives));
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref availablePoints, default(ForPrimitives));
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref ownedMask, default(ForPrimitives));
			int num4 = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num4, default(ForPrimitives));
			kind = (ProgressionNetworkMessageKind)num2;
			upgradeId = (ProgressionUpgradeId)num3;
			rejectReason = (ProgressionRejectReason)num4;
		}
	}
	internal sealed class ProgressionRuntime : MonoBehaviour
	{
		private ProgressionService? _service;

		public void Initialize(ProgressionService service)
		{
			_service = service;
		}

		private void Update()
		{
			if (_service != null)
			{
				_service.HandleUiKeyInput("runtime");
			}
		}
	}
	internal sealed class ProgressionService
	{
		private readonly ManualLogSource _logger;

		private readonly ConfigService _config;

		private readonly GameplayInputService _gameplayInput;

		private readonly ProgressionNetworkService _network;

		private readonly ModifierRegistry _modifierRegistry;

		private readonly PlayerStatApplier _statApplier;

		private readonly Dictionary<ulong, ProgressionPlayerState> _states = new Dictionary<ulong, ProgressionPlayerState>();

		private readonly Dictionary<ulong, bool> _hostAliveByClientId = new Dictionary<ulong, bool>();

		private ProgressionRuntime? _runtime;

		private ProgressionUiController? _ui;

		private GameObject? _runtimeHost;

		private bool? _wasLocalPlayerAlive;

		private ulong? _observedPlayerId;

		private ulong? _requestedStateClientId;

		private bool _localRequestPending;

		private string _lastRejectedReason = string.Empty;

		private int _lastOpenKeyFrame = -1;

		private int _lastCloseKeyFrame = -1;

		public IReadOnlyList<UpgradeDefinition> Upgrades => (from modifier in _modifierRegistry.GetAvailable(EffectTargetKind.Player)
			select modifier.ToUpgradeDefinition()).ToArray();

		internal KeyCode OpenKey => _config.ProgressionOpenKey;

		public event Action? StateChanged;

		public ProgressionService(ManualLogSource logger, ConfigService config, GameplayInputService gameplayInput, ProgressionNetworkService network, ModifierRegistry modifierRegistry)
		{
			_logger = logger;
			_config = config;
			_gameplayInput = gameplayInput;
			_network = network;
			_modifierRegistry = modifierRegistry;
			_statApplier = new PlayerStatApplier(logger);
		}

		public void Initialize()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			if (!_config.ProgressionEnabled)
			{
				_logger.LogInfo((object)"[HDM:Progression] Progression prototype disabled by config.");
				return;
			}
			_network.Initialize(OnHostRequestReceived, OnSnapshotReceived, OnRejectReceived);
			_runtimeHost = new GameObject("HDM_ProgressionRuntime");
			Object.DontDestroyOnLoad((Object)(object)_runtimeHost);
			_runtime = _runtimeHost.AddComponent<ProgressionRuntime>();
			_runtime.Initialize(this);
			_logger.LogInfo((object)"[HDM:Progression] Runtime initialized with host-authoritative networking.");
		}

		public void Shutdown()
		{
			CloseUi("shutdown");
			_ui?.Destroy();
			_ui = null;
			if (_runtimeHost != null)
			{
				Object.Destroy((Object)(object)_runtimeHost);
				_runtimeHost = null;
				_runtime = null;
			}
			_network.Shutdown();
			_statApplier.ForgetPlayer();
			_states.Clear();
			_hostAliveByClientId.Clear();
		}

		public void Tick(StartOfRound round, float deltaTime)
		{
			if (!_config.ProgressionEnabled)
			{
				return;
			}
			_network.Tick();
			HandleUiKeyInput("lifecycle");
			if (IsHostAuthority())
			{
				TickHostPlayerStates(round);
			}
			PlayerControllerB val = ResolveLocalPlayer(round);
			if (val == null || !val.isPlayerControlled)
			{
				_wasLocalPlayerAlive = null;
				_observedPlayerId = null;
				_statApplier.ForgetPlayer();
				return;
			}
			ulong localClientId = GetLocalClientId(val);
			EnsureLocalStateRequested(localClientId);
			if (_observedPlayerId != val.playerClientId)
			{
				_observedPlayerId = val.playerClientId;
				_wasLocalPlayerAlive = !val.isPlayerDead;
				_statApplier.ForgetPlayer();
				_logger.LogInfo((object)$"[HDM:Progression] Observing local player {val.playerClientId}; clientId={localClientId}.");
			}
			bool flag = !val.isPlayerDead;
			if (_wasLocalPlayerAlive == true && !flag)
			{
				if (_config.ProgressionResetRunProgressOnDeath)
				{
					if (IsHostAuthority())
					{
						ResetRunForClient(localClientId, "local death", notifyClient: true);
					}
					else
					{
						RequestResetRun("local death");
					}
				}
				CloseUi("death");
			}
			_wasLocalPlayerAlive = flag;
			if (flag)
			{
				ApplyLocalStateToPlayer(val, "tick");
			}
		}

		public void ToggleUi()
		{
			if (!_config.ProgressionEnabled)
			{
				return;
			}
			ProgressionUiController ui = _ui;
			if (ui != null && ui.IsOpen)
			{
				CloseUi("toggle");
				return;
			}
			PlayerControllerB player = ResolveLocalPlayer(Object.FindObjectOfType<StartOfRound>());
			if (!CanOpenUi(player, out string reason))
			{
				_logger.LogInfo((object)("[HDM:Progression] Progression UI blocked: " + reason + "."));
				return;
			}
			if (_ui == null)
			{
				_ui = new ProgressionUiController(_logger, this, _gameplayInput);
			}
			_ui.Open(player);
			_logger.LogInfo((object)"[HDM:Progression] Progression UI opened.");
		}

		public void OnOpenKeyPressed()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			_logger.LogInfo((object)$"[HDM:Progression] Open key pressed: key={_config.ProgressionOpenKey}.");
			ToggleUi();
		}

		public void HandleUiKeyInput(string source)
		{
			ProgressionUiController ui = _ui;
			if (ui == null || !ui.IsOpen || !HandleCloseKeyInput(source))
			{
				HandleOpenKeyInput(source);
			}
		}

		public void HandleOpenKeyInput(string source)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (Time.frameCount != _lastOpenKeyFrame && WasOpenKeyPressed())
			{
				_lastOpenKeyFrame = Time.frameCount;
				_logger.LogInfo((object)$"[HDM:Progression] Open key input detected: key={_config.ProgressionOpenKey}, source={source}.");
				ToggleUi();
			}
		}

		public void CloseUi(string reason)
		{
			if (_ui != null && _ui.IsOpen)
			{
				_ui.Close(reason);
				_logger.LogInfo((object)("[HDM:Progression] Progression UI closed: reason='" + reason + "'."));
			}
		}

		public bool TryPurchaseUpgrade(string upgradeId)
		{
			return RequestPurchaseForLocalPlayer(upgradeId);
		}

		public bool RequestPurchaseForLocalPlayer(string upgradeId)
		{
			UpgradeDefinition upgradeDefinition = FindUpgrade(upgradeId);
			if (upgradeDefinition == null)
			{
				_lastRejectedReason = "unknown upgrade " + upgradeId;
				_logger.LogWarning((object)("[HDM:Progression] Purchase request failed: unknown upgrade '" + upgradeId + "'."));
				NotifyStateChanged();
				return false;
			}
			return RequestPurchaseForLocalPlayer(upgradeDefinition.NetworkId);
		}

		public bool RequestPurchaseForLocalPlayer(ProgressionUpgradeId upgradeId)
		{
			if (_localRequestPending)
			{
				_logger.LogInfo((object)$"[HDM:Progression] Purchase request ignored while pending: upgrade={upgradeId}.");
				return false;
			}
			_lastRejectedReason = string.Empty;
			if (IsHostAuthority())
			{
				ulong localClientId = GetLocalClientId(ResolveLocalPlayer(Object.FindObjectOfType<StartOfRound>()));
				return ApplyPurchase(localClientId, upgradeId);
			}
			if (!_network.TrySendPurchaseRequest(upgradeId))
			{
				_lastRejectedReason = ProgressionRejectReason.NetworkUnavailable.ToString();
				_logger.LogInfo((object)$"[HDM:Progression] Purchase request blocked: upgrade={upgradeId}, reason=network unavailable.");
				NotifyStateChanged();
				return false;
			}
			_localRequestPending = true;
			_logger.LogInfo((object)$"[HDM:Progression] Sent purchase request: upgrade={upgradeId}, role={_network.DescribeRole()}.");
			NotifyStateChanged();
			return true;
		}

		public bool ApplyPurchase(ulong clientId, ProgressionUpgradeId upgradeId)
		{
			if (!IsHostAuthority())
			{
				Reject(clientId, upgradeId, ProgressionRejectReason.NotHost);
				return false;
			}
			UpgradeDefinition upgradeDefinition = FindUpgrade(upgradeId);
			if (upgradeDefinition == null)
			{
				Reject(clientId, upgradeId, ProgressionRejectReason.UnknownUpgrade);
				return false;
			}
			if (!upgradeDefinition.Available)
			{
				Reject(clientId, upgradeId, ProgressionRejectReason.Unavailable);
				return false;
			}
			ProgressionPlayerState progressionPlayerState = EnsureState(clientId);
			if (progressionPlayerState.IsOwned(upgradeId))
			{
				Reject(clientId, upgradeId, ProgressionRejectReason.AlreadyOwned);
				return false;
			}
			if (!progressionPlayerState.TryPurchase(upgradeId, upgradeDefinition.Cost))
			{
				Reject(clientId, upgradeId, ProgressionRejectReason.NotEnoughPoints);
				return false;
			}
			_logger.LogInfo((object)$"[HDM:Progression] Host accepted purchase: client={clientId}, upgrade={upgradeId}, points={progressionPlayerState.AvailablePoints}.");
			SyncStateToClient(clientId, "purchase");
			return true;
		}

		public void ResetRunProgress(string reason)
		{
			if (IsHostAuthority())
			{
				ResetRunForClient(GetLocalClientId(ResolveLocalPlayer(Object.FindObjectOfType<StartOfRound>())), reason, notifyClient: true);
			}
			else
			{
				RequestResetRun(reason);
			}
		}

		public void LogStatus(string reason)
		{
			ulong localClientId = GetLocalClientId(ResolveLocalPlayer(Object.FindObjectOfType<StartOfRound>()));
			ProgressionPlayerState stateOrDefault = GetStateOrDefault(localClientId);
			string[] value = stateOrDefault.OwnedUpgradeIds.Select((ProgressionUpgradeId upgradeId) => upgradeId.ToString()).ToArray();
			PlayerControllerB val = ResolveLocalPlayer(Object.FindObjectOfType<StartOfRound>());
			string text = ((val == null) ? "none" : $"id={val.playerClientId}, controlled={val.isPlayerControlled}, dead={val.isPlayerDead}, health={val.health}, move={val.movementSpeed:0.00}, sprintTime={val.sprintTime:0.00}, grab={val.grabDistance:0.00}");
			_logger.LogInfo((object)string.Format("[HDM:Progression] Status: reason='{0}', role={1}, localClient={2}, points={3}/{4}, owned=[{5}], pending={6}, rejected='{7}', player={8}.", reason, _network.DescribeRole(), localClientId, stateOrDefault.AvailablePoints, _config.ProgressionDevStartingPoints, string.Join(",", value), _localRequestPending, _lastRejectedReason, text));
		}

		public void LogSyncStatus(string reason)
		{
			string[] value = (from state in _states.Values
				orderby state.ClientId
				select $"{state.ClientId}:points={state.AvailablePoints},mask={state.ToOwnedMask()}").ToArray();
			_logger.LogInfo((object)string.Format("[HDM:Progression] SyncStatus: reason='{0}', role={1}, states=[{2}], pending={3}, lastNetwork='{4}'.", reason, _network.DescribeRole(), string.Join("; ", value), _localRequestPending, _network.LastMessage));
		}

		public ProgressionSnapshot CreateSnapshot()
		{
			return new ProgressionSnapshot(GetStateOrDefault(GetLocalClientId(ResolveLocalPlayer(Object.FindObjectOfType<StartOfRound>()))).AvailablePoints, _config.ProgressionDevStartingPoints, _localRequestPending, _lastRejectedReason);
		}

		public bool IsOwned(string upgradeId)
		{
			UpgradeDefinition upgradeDefinition = FindUpgrade(upgradeId);
			if (upgradeDefinition != null)
			{
				return IsOwned(upgradeDefinition.NetworkId);
			}
			return false;
		}

		public bool IsOwned(ProgressionUpgradeId upgradeId)
		{
			return GetStateOrDefault(GetLocalClientId(ResolveLocalPlayer(Object.FindObjectOfType<StartOfRound>()))).IsOwned(upgradeId);
		}

		public int GetRank(string upgradeId)
		{
			return IsOwned(upgradeId) ? 1 : 0;
		}

		private void OnHostRequestReceived(ulong senderClientId, ProgressionNetworkMessageKind kind, ProgressionUpgradeId upgradeId)
		{
			if (!IsHostAuthority())
			{
				_logger.LogInfo((object)$"[HDM:Progression] Ignored host request on non-host: sender={senderClientId}, kind={kind}.");
				return;
			}
			_logger.LogInfo((object)$"[HDM:Progression] Host received request: sender={senderClientId}, kind={kind}, upgrade={upgradeId}.");
			switch (kind)
			{
			case ProgressionNetworkMessageKind.PurchaseRequest:
				ApplyPurchase(senderClientId, upgradeId);
				break;
			case ProgressionNetworkMessageKind.ResetRequest:
				ResetRunForClient(senderClientId, "network reset request", notifyClient: true);
				break;
			case ProgressionNetworkMessageKind.RequestState:
				SyncStateToClient(senderClientId, "network state request");
				break;
			}
		}

		private void OnSnapshotReceived(ProgressionNetworkSnapshot snapshot)
		{
			ulong localClientId = GetLocalClientId(ResolveLocalPlayer(Object.FindObjectOfType<StartOfRound>()));
			if (snapshot.TargetClientId != localClientId)
			{
				_logger.LogInfo((object)$"[HDM:Progression] Ignored snapshot for another client: target={snapshot.TargetClientId}, local={localClientId}.");
				return;
			}
			EnsureState(snapshot.TargetClientId).ReplaceFromSnapshot(snapshot.AvailablePoints, snapshot.OwnedMask);
			_localRequestPending = false;
			_lastRejectedReason = string.Empty;
			_logger.LogInfo((object)$"[HDM:Progression] Applied host snapshot: client={snapshot.TargetClientId}, points={snapshot.AvailablePoints}, mask={snapshot.OwnedMask}.");
			ApplyLocalStateToCurrentPlayer("snapshot");
			NotifyStateChanged();
		}

		private void OnRejectReceived(ProgressionNetworkReject reject)
		{
			ulong localClientId = GetLocalClientId(ResolveLocalPlayer(Object.FindObjectOfType<StartOfRound>()));
			if (reject.TargetClientId == localClientId)
			{
				_localRequestPending = false;
				_lastRejectedReason = reject.Reason.ToString();
				_logger.LogInfo((object)$"[HDM:Progression] Host rejected request: upgrade={reject.UpgradeId}, reason={reject.Reason}.");
				NotifyStateChanged();
			}
		}

		private void TickHostPlayerStates(StartOfRound round)
		{
			if (round.allPlayerScripts == null)
			{
				return;
			}
			PlayerControllerB[] allPlayerScripts = round.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (val != null && val.isPlayerControlled)
				{
					ulong playerClientId = val.playerClientId;
					EnsureState(playerClientId);
					bool flag = !val.isPlayerDead;
					if (_hostAliveByClientId.TryGetValue(playerClientId, out var value) && value && !flag && _config.ProgressionResetRunProgressOnDeath)
					{
						ResetRunForClient(playerClientId, "host observed death", notifyClient: true);
					}
					_hostAliveByClientId[playerClientId] = flag;
				}
			}
		}

		private void EnsureLocalStateRequested(ulong localClientId)
		{
			if (IsHostAuthority() || !_network.IsListening)
			{
				EnsureState(localClientId);
			}
			else if (_requestedStateClientId != localClientId && !_localRequestPending && _network.TrySendStateRequest())
			{
				_requestedStateClientId = localClientId;
				_localRequestPending = true;
				_logger.LogInfo((object)$"[HDM:Progression] Requested host progression state: client={localClientId}.");
				NotifyStateChanged();
			}
		}

		private void RequestResetRun(string reason)
		{
			if (!_localRequestPending)
			{
				_lastRejectedReason = string.Empty;
				if (!_network.TrySendResetRequest())
				{
					_lastRejectedReason = ProgressionRejectReason.NetworkUnavailable.ToString();
					_logger.LogInfo((object)("[HDM:Progression] Reset request blocked: reason='" + reason + "', network unavailable."));
					NotifyStateChanged();
				}
				else
				{
					_localRequestPending = true;
					_logger.LogInfo((object)("[HDM:Progression] Sent reset request: reason='" + reason + "'."));
					NotifyStateChanged();
				}
			}
		}

		private void ResetRunForClient(ulong clientId, string reason, bool notifyClient)
		{
			EnsureState(clientId).Reset(_config.ProgressionDevStartingPoints);
			_logger.LogInfo((object)$"[HDM:Progression] Host reset run progression: client={clientId}, reason='{reason}'.");
			if (notifyClient)
			{
				SyncStateToClient(clientId, reason);
			}
		}

		private void SyncStateToClient(ulong clientId, string reason)
		{
			ProgressionPlayerState state = EnsureState(clientId);
			if (clientId == _network.LocalClientId || !_network.IsListening)
			{
				_localRequestPending = false;
				_lastRejectedReason = string.Empty;
				ApplyLocalStateToCurrentPlayer("host local " + reason);
				NotifyStateChanged();
			}
			else
			{
				_network.TrySendSnapshot(clientId, state);
			}
		}

		private void Reject(ulong clientId, ProgressionUpgradeId upgradeId, ProgressionRejectReason reason)
		{
			_logger.LogInfo((object)$"[HDM:Progression] Host rejected purchase: client={clientId}, upgrade={upgradeId}, reason={reason}.");
			if (clientId == _network.LocalClientId || !_network.IsListening)
			{
				_localRequestPending = false;
				_lastRejectedReason = reason.ToString();
				NotifyStateChanged();
			}
			else
			{
				_network.TrySendReject(clientId, upgradeId, reason);
			}
		}

		private void ApplyLocalStateToCurrentPlayer(string reason)
		{
			PlayerControllerB val = ResolveLocalPlayer(Object.FindObjectOfType<StartOfRound>());
			if (val != null)
			{
				ApplyLocalStateToPlayer(val, reason);
			}
		}

		private void ApplyLocalStateToPlayer(PlayerControllerB player, string reason)
		{
			ProgressionPlayerState stateOrDefault = GetStateOrDefault(GetLocalClientId(player));
			if (!stateOrDefault.OwnedUpgradeIds.Any())
			{
				_statApplier.Restore(player, reason);
			}
			else
			{
				_statApplier.Apply(player, CalculateTotals(stateOrDefault), reason);
			}
		}

		private ProgressionPlayerState EnsureState(ulong clientId)
		{
			if (_states.TryGetValue(clientId, out ProgressionPlayerState value))
			{
				return value;
			}
			value = new ProgressionPlayerState(clientId, _config.ProgressionDevStartingPoints);
			_states[clientId] = value;
			_logger.LogInfo((object)$"[HDM:Progression] Created session progression state: client={clientId}, points={value.AvailablePoints}.");
			return value;
		}

		private ProgressionPlayerState GetStateOrDefault(ulong clientId)
		{
			if (!_states.TryGetValue(clientId, out ProgressionPlayerState value))
			{
				return new ProgressionPlayerState(clientId, _config.ProgressionDevStartingPoints);
			}
			return value;
		}

		private bool IsHostAuthority()
		{
			if (_network.IsListening && !_network.IsServer)
			{
				return _network.IsHost;
			}
			return true;
		}

		private ulong GetLocalClientId(PlayerControllerB? player)
		{
			if (_network.IsListening)
			{
				return _network.LocalClientId;
			}
			return player?.playerClientId ?? 0;
		}

		private bool HandleCloseKeyInput(string source)
		{
			if (Time.frameCount == _lastCloseKeyFrame || !WasCloseKeyPressed(out string keyName))
			{
				return false;
			}
			_lastCloseKeyFrame = Time.frameCount;
			_logger.LogInfo((object)("[HDM:Progression] Close key input detected: key=" + keyName + ", source=" + source + "."));
			CloseUi("key " + keyName);
			return true;
		}

		private bool WasOpenKeyPressed()
		{
			if (!WasOpenKeyPressedByInputSystem())
			{
				return WasOpenKeyPressedByLegacyInput();
			}
			return true;
		}

		private bool WasCloseKeyPressed(out string keyName)
		{
			if (WasCloseKeyPressedByInputSystem(out keyName))
			{
				return true;
			}
			if (WasCloseKeyPressedByLegacyInput(out keyName))
			{
				return true;
			}
			keyName = string.Empty;
			return false;
		}

		private bool WasOpenKeyPressedByInputSystem()
		{
			//IL_0011: 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)
			//IL_001a: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_006d: 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_008b: Unknown result type (might be due to invalid IL or missing references)
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				return false;
			}
			KeyCode progressionOpenKey = _config.ProgressionOpenKey;
			if ((int)progressionOpenKey <= 111)
			{
				if ((int)progressionOpenKey == 96)
				{
					return ((ButtonControl)current.backquoteKey).wasPressedThisFrame;
				}
				if ((int)progressionOpenKey == 111)
				{
					return ((ButtonControl)current.oKey).wasPressedThisFrame;
				}
			}
			else
			{
				if ((int)progressionOpenKey == 112)
				{
					return ((ButtonControl)current.pKey).wasPressedThisFrame;
				}
				if ((int)progressionOpenKey == 287)
				{
					return ((ButtonControl)current.f6Key).wasPressedThisFrame;
				}
			}
			if (Enum.TryParse<Key>(((object)_config.ProgressionOpenKey/*cast due to .constrained prefix*/).ToString(), ignoreCase: true, out Key result))
			{
				return ((ButtonControl)current[result]).wasPressedThisFrame;
			}
			return false;
		}

		private bool WasOpenKeyPressedByLegacyInput()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return Input.GetKeyDown(_config.ProgressionOpenKey);
			}
			catch
			{
				return false;
			}
		}

		private static bool WasCloseKeyPressedByInputSystem(out string keyName)
		{
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				keyName = string.Empty;
				return false;
			}
			if (((ButtonControl)current.escapeKey).wasPressedThisFrame)
			{
				keyName = "Escape";
				return true;
			}
			if (((ButtonControl)current.tabKey).wasPressedThisFrame)
			{
				keyName = "Tab";
				return true;
			}
			keyName = string.Empty;
			return false;
		}

		private static bool WasCloseKeyPressedByLegacyInput(out string keyName)
		{
			try
			{
				if (Input.GetKeyDown((KeyCode)27))
				{
					keyName = "Escape";
					return true;
				}
				if (Input.GetKeyDown((KeyCode)9))
				{
					keyName = "Tab";
					return true;
				}
			}
			catch
			{
			}
			keyName = string.Empty;
			return false;
		}

		private ProgressionTotals CalculateTotals(ProgressionPlayerState state)
		{
			int num = 0;
			float num2 = 1f;
			float num3 = 1f;
			float num4 = 1f;
			foreach (ModifierDefinition item in _modifierRegistry.GetAvailable(EffectTargetKind.Player))
			{
				if (state.IsOwned(item.NetworkId))
				{
					switch (item.EffectKind)
					{
					case ProgressionEffectKind.HealthBonus:
						num += Mathf.RoundToInt(item.EffectValue);
						break;
					case ProgressionEffectKind.MovementMultiplier:
						num2 *= item.EffectValue;
						break;
					case ProgressionEffectKind.SprintTimeMultiplier:
						num3 *= item.EffectValue;
						break;
					case ProgressionEffectKind.GrabDistanceMultiplier:
						num4 *= item.EffectValue;
						break;
					}
				}
			}
			return new ProgressionTotals(num, num2, num3, num4);
		}

		private bool CanOpenUi(PlayerControllerB? player, out string reason)
		{
			if (player == null || !player.isPlayerControlled)
			{
				reason = "no controlled local player";
				return false;
			}
			if (player.isPlayerDead)
			{
				reason = "player is dead";
				return false;
			}
			if (player.inTerminalMenu)
			{
				reason = "terminal menu is open";
				return false;
			}
			if (player.inSpecialMenu)
			{
				reason = "special menu is open";
				return false;
			}
			if (player.quickMenuManager != null && player.quickMenuManager.isMenuOpen)
			{
				reason = "quick menu is open";
				return false;
			}
			reason = string.Empty;
			return true;
		}

		private UpgradeDefinition? FindUpgrade(string upgradeId)
		{
			return _modifierRegistry.FindAvailableById(upgradeId)?.ToUpgradeDefinition();
		}

		private UpgradeDefinition? FindUpgrade(ProgressionUpgradeId upgradeId)
		{
			return _modifierRegistry.FindAvailableByNetworkId(upgradeId)?.ToUpgradeDefinition();
		}

		private static PlayerControllerB? ResolveLocalPlayer(StartOfRound? round)
		{
			object obj = round?.localPlayerController;
			if (obj == null)
			{
				GameNetworkManager instance = GameNetworkManager.Instance;
				if (instance == null)
				{
					return null;
				}
				obj = instance.localPlayerController;
			}
			return (PlayerControllerB?)obj;
		}

		private void NotifyStateChanged()
		{
			this.StateChanged?.Invoke();
		}
	}
	internal sealed class ProgressionUiController
	{
		private sealed class UpgradeButtonBinding
		{
			public UpgradeDefinition Upgrade { get; }

			public Button Button { get; }

			public Image Background { get; }

			public Text Status { get; }

			public UpgradeButtonBinding(UpgradeDefinition upgrade, Button button, Image background, Text status)
			{
				Upgrade = upgrade;
				Button = button;
				Background = background;
				Status = status;
			}
		}

		private readonly ManualLogSource _logger;

		private readonly ProgressionService _service;

		private readonly GameplayInputService _gameplayInput;

		private readonly List<UpgradeButtonBinding> _buttons = new List<UpgradeButtonBinding>();

		private GameObject? _root;

		private Text? _pointsText;

		private PlayerControllerB? _owningPlayer;

		private bool _subscribed;

		public bool IsOpen
		{
			get
			{
				if (_root != null)
				{
					return _root.activeSelf;
				}
				return false;
			}
		}

		public ProgressionUiController(ManualLogSource logger, ProgressionService service, GameplayInputService gameplayInput)
		{
			_logger = logger;
			_service = service;
			_gameplayInput = gameplayInput;
		}

		public void Open(PlayerControllerB player)
		{
			EnsureUi();
			_owningPlayer = player;
			if (!_subscribed)
			{
				_service.StateChanged += Refresh;
				_subscribed = true;
			}
			_root.SetActive(true);
			_gameplayInput.LockUi(player, "progression");
			Refresh();
		}

		public void Close(string reason)
		{
			if (_root != null)
			{
				_root.SetActive(false);
			}
			_gameplayInput.UnlockUi("progression");
			_owningPlayer = null;
		}

		public void Destroy()
		{
			if (_subscribed)
			{
				_service.StateChanged -= Refresh;
				_subscribed = false;
			}
			if (_root != null)
			{
				Object.Destroy((Object)(object)_root);
				_root = null;
			}
			_buttons.Clear();
			_pointsText = null;
			_owningPlayer = null;
		}

		private void EnsureUi()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: 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_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			if (_root == null)
			{
				EnsureEventSystem();
				_root = new GameObject("HDM_ProgressionCanvas");
				Object.DontDestroyOnLoad((Object)(object)_root);
				Canvas obj = _root.AddComponent<Canvas>();
				obj.renderMode = (RenderMode)0;
				obj.sortingOrder = 6500;
				CanvasScaler obj2 = _root.AddComponent<CanvasScaler>();
				obj2.uiScaleMode = (ScaleMode)1;
				obj2.referenceResolution = new Vector2(1280f, 720f);
				obj2.matchWidthOrHeight = 0.5f;
				_root.AddComponent<GraphicRaycaster>();
				Stretch(CreatePanel(_root.transform, "Dim", new Color(0f, 0f, 0f, 0.78f)).GetComponent<RectTransform>());
				GameObject val = CreatePanel(_root.transform, "Tree", new Color(0.07f, 0.08f, 0.09f, 0.96f));
				RectTransform component = val.GetComponent<RectTransform>();
				component.anchorMin = new Vector2(0.5f, 0.5f);
				component.anchorMax = new Vector2(0.5f, 0.5f);
				component.pivot = new Vector2(0.5f, 0.5f);
				component.sizeDelta = new Vector2(980f, 560f);
				component.anchoredPosition = Vector2.zero;
				Place(((Graphic)CreateText(val.transform, "Title", "HDM RUN TRAINING", 28, (TextAnchor)3, Color.white)).rectTransform, 28f, -24f, 480f, 42f, new Vector2(0f, 1f), new Vector2(0f, 1f));
				_pointsText = CreateText(val.transform, "Points", string.Empty, 18, (TextAnchor)5, new Color(0.8f, 0.95f, 1f, 1f));
				Place(((Graphic)_pointsText).rectTransform, -210f, -26f, 170f, 34f, new Vector2(1f, 1f), new Vector2(1f, 1f));
				CreateActionButton(val.transform, "Close", "X", new Vector2(-44f, -28f), new Vector2(34f, 34f), delegate
				{
					_service.CloseUi("button");
				});
				CreateActionButton(val.transform, "Reset", "Reset Run", new Vector2(-132f, -514f), new Vector2(134f, 38f), delegate
				{
					_service.ResetRunProgress("ui reset");
				});
				CreateBranch(val.transform, "Grit", 28f);
				CreateBranch(val.transform, "Legs", 340f);
				CreateBranch(val.transform, "Utility", 652f);
				_root.SetActive(false);
				_logger.LogInfo((object)"[HDM:Progression] Progression UI created.");
			}
		}

		private void CreateBranch(Transform parent, string branch, float x)
		{
			//IL_0040: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			Place(((Graphic)CreateText(parent, branch + "_Header", branch.ToUpperInvariant(), 20, (TextAnchor)4, new Color(1f, 0.86f, 0.54f, 1f))).rectTransform, x, -92f, 280f, 34f, new Vector2(0f, 1f), new Vector2(0f, 1f));
			UpgradeDefinition[] array = _service.Upgrades.Where((UpgradeDefinition upgrade) => upgrade.Branch.Equals(branch, StringComparison.OrdinalIgnoreCase)).ToArray();
			for (int num = 0; num < array.Length; num++)
			{
				CreateUpgradeButton(parent, array[num], new Vector2(x, -140f - (float)num * 128f));
			}
		}

		private void CreateUpgradeButton(Transform parent, UpgradeDefinition upgrade, Vector2 position)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreatePanel(parent, "Upgrade_" + upgrade.Id, new Color(0.15f, 0.17f, 0.18f, 1f));
			Place(val.GetComponent<RectTransform>(), position.x, position.y, 280f, 104f, new Vector2(0f, 1f), new Vector2(0f, 1f));
			Button val2 = val.AddComponent<Button>();
			((Selectable)val2).transition = (Transition)1;
			((Selectable)val2).colors = CreateButtonColors();
			((UnityEvent)val2.onClick).AddListener((UnityAction)delegate
			{
				_service.RequestPurchaseForLocalPlayer(upgrade.Id);
			});
			Place(((Graphic)CreateText(val.transform, "Title", upgrade.Title, 17, (TextAnchor)0, Color.white)).rectTransform, 14f, -10f, 252f, 28f, new Vector2(0f, 1f), new Vector2(0f, 1f));
			Place(((Graphic)CreateText(val.transform, "Description", upgrade.Description, 13, (TextAnchor)0, new Color(0.78f, 0.84f, 0.86f, 1f))).rectTransform, 14f, -42f, 252f, 30f, new Vector2(0f, 1f), new Vector2(0f, 1f));
			Text val3 = CreateText(val.transform, "Status", string.Empty, 13, (TextAnchor)6, new Color(0.8f, 0.95f, 1f, 1f));
			Place(((Graphic)val3).rectTransform, 14f, -78f, 252f, 20f, new Vector2(0f, 1f), new Vector2(0f, 1f));
			_buttons.Add(new UpgradeButtonBinding(upgrade, val2, val.GetComponent<Image>(), val3));
		}

		private void Refresh()
		{
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			if (_root == null)
			{
				return;
			}
			ProgressionSnapshot progressionSnapshot = _service.CreateSnapshot();
			if (_pointsText != null)
			{
				string arg = (progressionSnapshot.IsRequestPending ? " - Syncing" : (string.IsNullOrWhiteSpace(progressionSnapshot.LastRejectedReason) ? string.Empty : (" - Rejected: " + progressionSnapshot.LastRejectedReason)));
				_pointsText.text = $"Points: {progressionSnapshot.AvailablePoints}/{progressionSnapshot.StartingPoints}{arg}";
			}
			foreach (UpgradeButtonBinding button in _buttons)
			{
				bool flag = _service.IsOwned(button.Upgrade.Id);
				bool flag2 = progressionSnapshot.AvailablePoints >= button.Upgrade.Cost;
				bool interactable = button.Upgrade.Available && !flag && flag2 && !progressionSnapshot.IsRequestPending;
				((Selectable)button.Button).interactable = interactable;
				button.Status.text = FormatStatus(button.Upgrade, flag, flag2, progressionSnapshot.IsRequestPending);
				((Graphic)button.Background).color = SelectBackground(button.Upgrade, flag, flag2);
			}
		}

		private static string FormatStatus(UpgradeDefinition upgrade, bool owned, bool affordable, bool pending)
		{
			if (!upgrade.Available)
			{
				return "Unavailable - experimental";
			}
			if (pending && !owned)
			{
				return "Waiting for host";
			}
			if (owned)
			{
				return "Owned - rank 1/1";
			}
			if (!affordable)
			{
				return $"Locked - cost {upgrade.Cost}";
			}
			return $"Available - cost {upgrade.Cost}";
		}

		private static Color SelectBackground(UpgradeDefinition upgrade, bool owned, bool affordable)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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)
			if (!upgrade.Available)
			{
				return new Color(0.11f, 0.11f, 0.12f, 0.88f);
			}
			if (owned)
			{
				return new Color(0.12f, 0.34f, 0.25f, 1f);
			}
			if (!affordable)
			{
				return new Color(0.18f, 0.15f, 0.14f, 1f);
			}
			return new Color(0.15f, 0.18f, 0.21f, 1f);
		}

		private static GameObject CreatePanel(Transform parent, string name, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			((Transform)val.AddComponent<RectTransform>()).localScale = Vector3.one;
			((Graphic)val.AddComponent<Image>()).color = color;
			return val;
		}

		private static Text CreateText(Transform parent, string name, string value, int fontSize, TextAnchor alignment, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			((Transform)val.AddComponent<RectTransform>()).localScale = Vector3.one;
			Text obj = val.AddComponent<Text>();
			obj.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
			obj.text = value;
			obj.fontSize = fontSize;
			obj.alignment = alignment;
			((Graphic)obj).color = color;
			obj.horizontalOverflow = (HorizontalWrapMode)0;
			obj.verticalOverflow = (VerticalWrapMode)0;
			return obj;
		}

		private static void CreateActionButton(Transform parent, string name, string label, Vector2 position, Vector2 size, Action onClick)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = CreatePanel(parent, name, new Color(0.2f, 0.23f, 0.25f, 1f));
			Place(obj.GetComponent<RectTransform>(), position.x, position.y, size.x, size.y, new Vector2(1f, 1f), new Vector2(1f, 1f));
			Button obj2 = obj.AddComponent<Button>();
			((Selectable)obj2).colors = CreateButtonColors();
			((UnityEvent)obj2.onClick).AddListener((UnityAction)delegate
			{
				onClick();
			});
			Stretch(((Graphic)CreateText(obj.transform, "Label", label, 14, (TextAnchor)4, Color.white)).rectTransform);
		}

		private static ColorBlock CreateButtonColors()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: 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)
			//IL_0028: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			ColorBlock defaultColorBlock = ColorBlock.defaultColorBlock;
			((ColorBlock)(ref defaultColorBlock)).normalColor = Color.white;
			((ColorBlock)(ref defaultColorBlock)).highlightedColor = new Color(0.9f, 0.96f, 1f, 1f);
			((ColorBlock)(ref defaultColorBlock)).pressedColor = new Color(0.68f, 0.82f, 0.94f, 1f);
			((ColorBlock)(ref defaultColorBlock)).disabledColor = new Color(0.5f, 0.5f, 0.5f, 0.8f);
			((ColorBlock)(ref defaultColorBlock)).colorMultiplier = 1f;
			return defaultColorBlock;
		}

		private static void Place(RectTransform rect, float x, float y, float width, float height, Vector2 anchor, Vector2 pivot)
		{
			//IL_0001: 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_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = anchor;
			rect.anchorMax = anchor;
			rect.pivot = pivot;
			rect.anchoredPosition = new Vector2(x, y);
			rect.sizeDelta = new Vector2(width, height);
		}

		private static void Stretch(RectTransform rect)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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)
			rect.anchorMin = Vector2.zero;
			rect.anchorMax = Vector2.one;
			rect.offsetMin = Vector2.zero;
			rect.offsetMax = Vector2.zero;
		}

		private static void EnsureEventSystem()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (Object.FindObjectOfType<EventSystem>() == null)
			{
				GameObject val = new GameObject("HDM_EventSystem");
				Object.DontDestroyOnLoad((Object)val);
				val.AddComponent<EventSystem>();
				val.AddComponent<StandaloneInputModule>();
			}
		}
	}
}
namespace HorrorDirectorMod.Observation
{
	internal sealed class GameObservation
	{
		public DateTime ObservedAt { get; }

		public RoundObservation Round { get; }

		public TeamObservation Team { get; }

		public VoiceObservation Voice { get; }

		public IReadOnlyList<PlayerObservation> Players { get; }

		public GameObservation(DateTime observedAt, RoundObservation round, TeamObservation team, VoiceObservation voice, IReadOnlyList<PlayerObservation> players)
		{
			ObservedAt = observedAt;
			Round = round;
			Team = team;
			Voice = voice;
			Players = players;
		}
	}
	internal sealed class GameObserver : IGameObserver
	{
		private readonly struct VoicePlayerObservation
		{
			public bool IsSpeaking { get; }

			public float Amplitude { get; }

			public VoicePlayerObservation(bool isSpeaking, float amplitude)
			{
				IsSpeaking = isSpeaking;
				Amplitude = amplitude;
			}
		}

		private readonly struct VoiceActivitySample
		{
			public DateTime ObservedAt { get; }

			public bool HasSpeakingPlayers { get; }

			public VoiceActivitySample(DateTime observedAt, bool hasSpeakingPlayers)
			{
				ObservedAt = observedAt;
				HasSpeakingPlayers = hasSpeakingPlayers;
			}
		}

		private readonly ManualLogSource _logger;

		private readonly ConfigService _config;

		private readonly Queue<VoiceActivitySample> _voiceSamples = new Queue<VoiceActivitySample>();

		private bool _voiceAvailabilityLogged;

		private DateTime? _lastVoiceActivityAt;

		private float _previousSplitScore;

		private IReadOnlyList<ulong> _previousIsolatedPlayerIds = new ulong[0];

		public GameObserver(ManualLogSource logger, ConfigService config)
		{
			_logger = logger;
			_config = config;
		}

		public GameObservation Observe(StartOfRound? round)
		{
			DateTime utcNow = DateTime.UtcNow;
			NetworkManager singleton = NetworkManager.Singleton;
			IReadOnlyDictionary<string, VoicePlayerObservation> voiceStates = ObserveVoiceStates(round);
			IReadOnlyList<PlayerObservation> players = ObservePlayers(round, voiceStates);
			TeamObservation teamObservation = ObserveTeam(players);
			VoiceObservation voice = ObserveVoice(utcNow, round, voiceStates);
			RoundObservation round2 = new RoundObservation(singleton != null && singleton.IsHost, singleton != null && singleton.IsServer, singleton != null && singleton.IsClient, round != null, Math.Max(0, (round != null) ? (round.connectedPlayersAmount + 1) : 0), GetLocalPlayerName(round));
			_previousSplitScore = teamObservation.SplitScore;
			_previousIsolatedPlayerIds = teamObservation.IsolatedPlayerIds;
			return new GameObservation(utcNow, round2, teamObservation, voice, players);
		}

		public void LogObservation(GameObservation observation, string reason)
		{
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			_logger.LogInfo((object)$"[HDM:Observer] {reason}: controlled={observation.Team.ControlledCount}, alive={observation.Team.AliveCount}, aliveInside={observation.Team.AliveInsideFactoryCount}, split={observation.Team.SplitScore:0.0}, previousSplit={_previousSplitScore:0.0}, isolated={observation.Team.IsolatedPlayerIds.Count}, talkRatio={observation.Voice.TeamTalkRatio:0.00}, silence={observation.Voice.SilenceSeconds:0.0}s, voiceSource={observation.Voice.Source}.");
			foreach (PlayerObservation player in observation.Players)
			{
				if (player.IsControlled)
				{
					string text = ((!player.IsSpeaking.HasValue) ? "unknown" : (player.IsSpeaking.Value ? "true" : "false"));
					_logger.LogInfo((object)$"[HDM:Observer] Player {player.PlayerId}: name='{player.Name}', alive={player.IsAlive}, insideFactory={player.IsInsideFactory}, inShip={player.IsInShip}, speaking={text}, amp={FormatNullable(player.VoiceAmplitude)}, pos={FormatVector(player.Position)}.");
				}
			}
		}

		private IReadOnlyList<PlayerObservation> ObservePlayers(StartOfRound? round, IReadOnlyDictionary<string, VoicePlayerObservation> voiceStates)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerObservation> list = new List<PlayerObservation>();
			PlayerControllerB[] array = round?.allPlayerScripts;
			if (array == null)
			{
				return list;
			}
			PlayerControllerB[] array2 = array;
			foreach (PlayerControllerB val in array2)
			{
				if (val != null)
				{
					string playerName = GetPlayerName(val);
					VoicePlayerObservation value;
					bool flag = voiceStates.TryGetValue(playerName, out value);
					ulong playerClientId = val.playerClientId;
					bool isPlayerControlled = val.isPlayerControlled;
					bool isAlive = !val.isPlayerDead;
					bool isInsideFactory = val.isInsideFactory;
					bool isInHangarShipRoom = val.isInHangarShipRoom;
					Transform transform = ((Component)val).transform;
					list.Add(new PlayerObservation(playerClientId, playerName, isPlayerControlled, isAlive, isInsideFactory, isInHangarShipRoom, (transform != null) ? transform.position : Vector3.zero, flag ? new bool?(value.IsSpeaking) : ((bool?)null), flag ? new float?(value.Amplitude) : ((float?)null)));
				}
			}
			return list;
		}

		private TeamObservation ObserveTeam(IReadOnlyList<PlayerObservation> players)
		{
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			List<PlayerObservation> list = new List<PlayerObservation>();
			foreach (PlayerObservation player in players)
			{
				if (!player.IsControlled)
				{
					continue;
				}
				num++;
				if (player.IsAlive)
				{
					num2++;
					list.Add(player);
					if (player.IsInsideFactory)
					{
						num3++;
					}
				}
			}
			return new TeamObservation(num, num2, num3, CalculateTeamSplitScore(list), FindIsolatedPlayerIds(list, _config.IsolationDistance));
		}

		private IReadOnlyDictionary<string, VoicePlayerObservation> ObserveVoiceStates(StartOfRound? round)
		{
			Dictionary<string, VoicePlayerObservation> dictionary = new Dictionary<string, VoicePlayerObservation>(StringComparer.OrdinalIgnoreCase);
			if (!_config.VoiceProbeEnabled)
			{
				return dictionary;
			}
			try
			{
				DissonanceComms val = Object.FindObjectOfType<DissonanceComms>();
				if (val == null)
				{
					LogVoiceAvailabilityOnce("[HDM:Voice] DissonanceComms not found yet.");
					return dictionary;
				}
				LogVoiceAvailabilityOnce("[HDM:Voice] DissonanceComms found. LocalPlayerName='" + val.LocalPlayerName + "'.");
				for (int i = 0; i < val.Players.Count; i++)
				{
					VoicePlayerState val2 = val.Players[i];
					if (val2 != null && !string.IsNullOrWhiteSpace(val2.Name))
					{
						dictionary[val2.Name] = new VoicePlayerObservation(val2.IsSpeaking, val2.Amplitude);
					}
				}
				string text = ResolveLocalPlayerName(round, val);
				if (!string.IsNullOrWhiteSpace(text) && !dictionary.ContainsKey(text) && val.Players.Count == 1)
				{
					VoicePlayerState val3 = val.Players[0];
					dictionary[text] = new VoicePlayerObservation(val3.IsSpeaking, val3.Amplitude);
				}
			}
			catch (Exception ex)
			{
				_logger.LogWarning((object)("[HDM:Voice] Dissonance observation failed: " + ex.GetType().Name + ": " + ex.Message));
			}
			return dictionary;
		}

		private VoiceObservation ObserveVoice(DateTime observedAt, StartOfRound? round, IReadOnlyDictionary<string, VoicePlayerObservation> voiceStates)
		{
			if (!_config.VoiceProbeEnabled)
			{
				return VoiceObservation.Disabled;
			}
			int num = 0;
			foreach (VoicePlayerObservation value in voiceStates.Values)
			{
				if (value.IsSpeaking)
				{
					num++;
				}
			}
			bool flag = num > 0;
			_voiceSamples.Enqueue(new VoiceActivitySample(observedAt, flag));
			TrimVoiceSamples(observedAt);
			if (flag)
			{
				_lastVoiceActivityAt = observedAt;
			}
			bool? localPlayerSpeaking = ResolveLocalSpeaking(round, voiceStates);
			string source = ((voiceStates.Count == 0) ? "dissonance_empty" : "dissonance");
			return new VoiceObservation(isAvailable: true, num, localPlayerSpeaking, CalculateTeamTalkRatio(), CalculateSilenceSeconds(observedAt), source);
		}

		private void TrimVoiceSamples(DateTime observedAt)
		{
			DateTime dateTime = observedAt - TimeSpan.FromSeconds(_config.VoiceActivityWindowSeconds);
			while (_voiceSamples.Count > 0 && _voiceSamples.Peek().ObservedAt < dateTime)
			{
				_voiceSamples.Dequeue();
			}
		}

		private float CalculateTeamTalkRatio()
		{
			if (_voiceSamples.Count == 0)
			{
				return 0f;
			}
			int num = 0;
			foreach (VoiceActivitySample voiceSample in _voiceSamples)
			{
				if (voiceSample.HasSpeakingPlayers)
				{
					num++;
				}
			}
			return (float)num / (float)_voiceSamples.Count;
		}

		private float CalculateSilenceSeconds(DateTime observedAt)
		{
			if (!_lastVoiceActivityAt.HasValue)
			{
				if (_voiceSamples.Count != 0)
				{
					return (float)(observedAt - _voiceSamples.Peek().ObservedAt).TotalSeconds;
				}
				return 0f;
			}
			return Math.Max(0f, (float)(observedAt - _lastVoiceActivityAt.Value).TotalSeconds);
		}

		private bool? ResolveLocalSpeaking(StartOfRound? round, IReadOnlyDictionary<string, VoicePlayerObservation> voiceStates)
		{
			string localPlayerName = GetLocalPlayerName(round);
			if (string.IsNullOrWhiteSpace(localPlayerName))
			{
				return null;
			}
			if (!voiceStates.TryGetValue(localPlayerName, out var value))
			{
				return null;
			}
			return value.IsSpeaking;
		}

		private static float CalculateTeamSplitScore(IReadOnlyList<PlayerObservation> players)
		{
			//IL_0029: 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)
			if (players.Count < 2)
			{
				return 0f;
			}
			float num = 0f;
			int num2 = 0;
			for (int i = 0; i < players.Count; i++)
			{
				for (int j = i + 1; j < players.Count; j++)
				{
					num += Vector3.Distance(players[i].Position, players[j].Position);
					num2++;
				}
			}
			if (num2 != 0)
			{
				return num / (float)num2;
			}
			return 0f;
		}

		private static IReadOnlyList<ulong> FindIsolatedPlayerIds(IReadOnlyList<PlayerObservation> players, float isolationDistance)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			List<ulong> list = new List<ulong>();
			if (players.Count < 2)
			{
				return list;
			}
			for (int i = 0; i < players.Count; i++)
			{
				float num = float.MaxValue;
				for (int j = 0; j < players.Count; j++)
				{
					if (i != j)
					{
						num = Math.Min(num, Vector3.Distance(players[i].Position, players[j].Position));
					}
				}
				if (num > isolationDistance)
				{
					list.Add(players[i].PlayerId);
				}
			}
			return list;
		}

		private void LogVoiceAvailabilityOnce(string message)
		{
			if (!_voiceAvailabilityLogged && _config.VoiceProbeLogComponentShape)
			{
				_voiceAvailabilityLogged = true;
				_logger.LogInfo((object)message);
			}
		}

		private static string ResolveLocalPlayerName(StartOfRound? round, DissonanceComms comms)
		{
			if (!string.IsNullOrWhiteSpace(comms.LocalPlayerName))
			{
				return comms.LocalPlayerName;
			}
			return GetLocalPlayerName(round);
		}

		private static string GetLocalPlayerName(StartOfRound? round)
		{
			PlayerControllerB val = round?.localPlayerController;
			if (val == null)
			{
				return string.Empty;
			}
			return GetPlayerName(val);
		}

		private static string GetPlayerName(PlayerControllerB player)
		{
			if (!string.IsNullOrWhiteSpace(player.playerUsername))
			{
				retur