using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HoldfastGame; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace AdminHelper { [BepInPlugin("com.ryannlt.adminhelper", "AdminHelper", "1.0.0")] public class AdminHelperMod : BaseUnityPlugin { public const string Guid = "com.ryannlt.adminhelper"; private readonly IsolationTracker _tracker = new IsolationTracker(); private readonly RingRenderer _rings = new RingRenderer(); private readonly Hotkey _hotkey = new Hotkey(); private readonly Hud _hud = new Hud(); private Driver _driver; private float _accumulator; private bool _wasInRound; private void Awake() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) Settings.Create(((BaseUnityPlugin)this).Config); _hotkey.ResetToDefault(); SceneManager.sceneLoaded += OnSceneLoaded; EnsureDriver(); Log.Info("Ready. Toggle key " + ((object)Settings.ResolveToggleKey()/*cast due to .constrained prefix*/).ToString() + ", RequireAdminLogin=" + Settings.RequireAdminLogin.Value); } private void OnDestroy() { Log.Info("plugin component destroyed"); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { Log.Info("scene loaded " + ((Scene)(ref scene)).name + " driverAlive=" + ((Object)(object)_driver != (Object)null)); EnsureDriver(); GameAccess.ClearSceneCache(); _tracker.Reset(); _rings.Destroy(); _accumulator = 0f; _wasInRound = false; } private void EnsureDriver() { if (!((Object)(object)_driver != (Object)null)) { _driver = Driver.Attach(this); } } internal void Tick() { Settings.PollForExternalEdits(); if (!Settings.Enabled.Value) { _rings.HideAll(); return; } _hotkey.Poll(); if (!GameAccess.InRound) { if (_wasInRound) { _tracker.Reset(); } _wasInRound = false; _rings.HideAll(); return; } _wasInRound = true; _accumulator += Time.deltaTime; float num = 1f / Mathf.Clamp(Settings.TickHz.Value, 1f, 30f); if (_accumulator >= num) { _tracker.Tick(_accumulator); _accumulator = 0f; } if (CanReveal() && _hotkey.Visible && Settings.ShowRings.Value) { _rings.Draw(_tracker.Watched); } else { _rings.HideAll(); } } internal void DrawGui() { if (Settings.Enabled.Value && _hotkey.Visible && GameAccess.InRound) { _hud.Draw(_tracker, CanReveal()); } } private static bool CanReveal() { return !Settings.RequireAdminLogin.Value || GameAccess.IsLoggedInAdmin; } } internal sealed class Driver : MonoBehaviour { internal AdminHelperMod Owner; internal static Driver Attach(AdminHelperMod owner) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown GameObject val = new GameObject("AdminHelper_Driver"); Object.DontDestroyOnLoad((Object)(object)val); Driver driver = val.AddComponent(); driver.Owner = owner; return driver; } private void Awake() { Log.Info("driver awake"); } private void OnDestroy() { Log.Info("driver destroyed"); } private void Update() { if (Owner != null) { Owner.Tick(); } } private void OnGUI() { if (Owner != null) { Owner.DrawGui(); } } } internal static class FormationDetector { private const int MaxScratch = 16; private static readonly float[] ScratchX = new float[16]; private static readonly float[] ScratchZ = new float[16]; private static readonly float[] ScratchDistanceSquared = new float[16]; public static bool IsInFormation(PlayerSnapshot self, List friendlies) { if (GameAccess.IsInsideOfficerLine(self.Player)) { return true; } int num = CollectNearest(self, friendlies, Settings.LineRadius.Value, Settings.LineMaxMates.Value); if (CountWithin(num, Settings.ClusterFormationRadius.Value) >= Settings.ClusterMinMates.Value) { return true; } if (num < Settings.LineMinMates.Value) { return false; } return PerpendicularSpread(self, num) <= Settings.LineResidual.Value; } private static int CollectNearest(PlayerSnapshot self, List friendlies, float radius, int max) { //IL_0041: 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_005b: 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_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) if (max > 16) { max = 16; } float num = radius * radius; int num2 = 0; for (int i = 0; i < friendlies.Count; i++) { PlayerSnapshot playerSnapshot = friendlies[i]; if (playerSnapshot.PlayerId == self.PlayerId) { continue; } float num3 = playerSnapshot.Position.x - self.Position.x; float num4 = playerSnapshot.Position.z - self.Position.z; float num5 = num3 * num3 + num4 * num4; if (!(num5 > num) && (num2 != max || !(num5 >= ScratchDistanceSquared[num2 - 1]))) { int num6 = ((num2 < max) ? num2 : (num2 - 1)); while (num6 > 0 && ScratchDistanceSquared[num6 - 1] > num5) { ScratchDistanceSquared[num6] = ScratchDistanceSquared[num6 - 1]; ScratchX[num6] = ScratchX[num6 - 1]; ScratchZ[num6] = ScratchZ[num6 - 1]; num6--; } ScratchDistanceSquared[num6] = num5; ScratchX[num6] = playerSnapshot.Position.x; ScratchZ[num6] = playerSnapshot.Position.z; if (num2 < max) { num2++; } } } return num2; } private static int CountWithin(int count, float radius) { float num = radius * radius; for (int i = 0; i < count; i++) { if (ScratchDistanceSquared[i] > num) { return i; } } return count; } private static float PerpendicularSpread(PlayerSnapshot self, int count) { //IL_0006: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) int num = count + 1; float num2 = self.Position.x; float num3 = self.Position.z; for (int i = 0; i < count; i++) { num2 += ScratchX[i]; num3 += ScratchZ[i]; } float num4 = num2 / (float)num; float num5 = num3 / (float)num; float xx = 0f; float xz = 0f; float zz = 0f; AccumulateCovariance(self.Position.x - num4, self.Position.z - num5, ref xx, ref xz, ref zz); for (int j = 0; j < count; j++) { AccumulateCovariance(ScratchX[j] - num4, ScratchZ[j] - num5, ref xx, ref xz, ref zz); } float num6 = xx + zz; float num7 = xx - zz; float num8 = Mathf.Sqrt(num7 * num7 + 4f * xz * xz); float num9 = Mathf.Max(0f, (num6 - num8) * 0.5f); return Mathf.Sqrt(num9 / (float)num); } private static void AccumulateCovariance(float dx, float dz, ref float xx, ref float xz, ref float zz) { xx += dx * dx; xz += dx * dz; zz += dz * dz; } } internal static class GameAccess { private static GameConsolePanel _consolePanel; private static float _nextConsoleLookup; public static ClientComponentReferenceManager Client => ClientComponentReferenceManager.ClientInstance; public static bool IsLoggedInAdmin => ClientRemoteConsoleAccessManager.loggedOn; public static bool InRound { get { ClientComponentReferenceManager client = Client; return (Object)(object)client != (Object)null && (Object)(object)client.clientRoundPlayerManager != (Object)null; } } public static Camera ActiveCamera { get { ClientComponentReferenceManager client = Client; if ((Object)(object)client == (Object)null || (Object)(object)client.ownerCameraManager == (Object)null) { return null; } Camera val = client.ownerCameraManager.ActiveCamera; if ((Object)(object)val == (Object)null) { val = client.ownerCameraManager.ownerCamera; } return val; } } public static int LocalPlayerId { get { ClientComponentReferenceManager client = Client; if ((Object)(object)client == (Object)null || (Object)(object)client.clientRoundPlayerManager == (Object)null) { return -1; } return ((RoundPlayer)(client.clientRoundPlayerManager.LocalPlayer?)).NetworkPlayerID ?? (-1); } } public static bool IsTyping { get { ClientComponentReferenceManager client = Client; if ((Object)(object)client != (Object)null && (Object)(object)client.clientChatHandler != (Object)null && client.clientChatHandler.isChatPaneOpened) { return true; } if ((Object)(object)_consolePanel == (Object)null && Time.unscaledTime >= _nextConsoleLookup) { _nextConsoleLookup = Time.unscaledTime + 2f; _consolePanel = Object.FindObjectOfType(); } return (Object)(object)_consolePanel != (Object)null && _consolePanel.Showing; } } public static void ClearSceneCache() { _consolePanel = null; _nextConsoleLookup = 0f; } public static void CollectPlayers(List into) { into.Clear(); ClientComponentReferenceManager client = Client; if ((Object)(object)client == (Object)null) { return; } ClientRoundPlayerManager clientRoundPlayerManager = client.clientRoundPlayerManager; if ((Object)(object)clientRoundPlayerManager == (Object)null) { return; } List roundPlayersList = clientRoundPlayerManager.roundPlayersList; for (int i = 0; i < roundPlayersList.Count; i++) { if (TrySnapshot((RoundPlayer)(object)roundPlayersList[i], out var snapshot)) { into.Add(snapshot); } } if (TrySnapshot((RoundPlayer)(object)clientRoundPlayerManager.LocalPlayer, out var snapshot2)) { into.Add(snapshot2); } } private static bool TrySnapshot(RoundPlayer player, out PlayerSnapshot snapshot) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) snapshot = default(PlayerSnapshot); if (player == null || (Object)(object)player.PlayerBase == (Object)null || !player.PlayerBase.SpawnedAndAlive) { return false; } if (player.PlayerTransformData == null || player.PlayerStartData == null) { return false; } PlayerBase playerBase = player.PlayerBase; snapshot.Player = player; snapshot.PlayerId = player.NetworkPlayerID; snapshot.Name = ResolveName(player); snapshot.Position = player.PlayerTransformData.position; snapshot.Faction = player.PlayerStartData.Faction; snapshot.Class = player.PlayerStartData.ClassType; snapshot.IsCavalry = playerBase.IsCavalry; snapshot.IsArtillery = playerBase.IsArty; return true; } private static string ResolveName(RoundPlayer player) { RoundPlayerInformation playerRoundInformation = player.PlayerRoundInformation; if (playerRoundInformation == null || playerRoundInformation.InitialDetails == null) { return "Player " + player.NetworkPlayerID; } PlayerInitialDetails initialDetails = playerRoundInformation.InitialDetails; string text = (string.IsNullOrEmpty(initialDetails.DisplayName) ? initialDetails.Name : initialDetails.DisplayName); if (string.IsNullOrEmpty(text)) { return "Player " + player.NetworkPlayerID; } return text; } public static bool IsInsideOfficerLine(RoundPlayer player) { ClientComponentReferenceManager client = Client; if ((Object)(object)client == (Object)null || (Object)(object)client.clientHighCommandOrderManager == (Object)null) { return false; } return ((HighCommandOrderManager)client.clientHighCommandOrderManager).IsPlayerInsideAnyOfficerLine(player); } } internal sealed class Hotkey { private bool _visible; public bool Visible => _visible; public void ResetToDefault() { _visible = Settings.StartHudVisible.Value; } public void Poll() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (!GameAccess.IsTyping && Input.GetKeyDown(Settings.ResolveToggleKey())) { _visible = !_visible; } } } internal sealed class Hud { private static readonly Color WatchColour = new Color(1f, 0.82f, 0.15f); private static readonly Color FlagColour = new Color(1f, 0.35f, 0.25f); private static readonly Color InfoColour = new Color(0.85f, 0.88f, 0.92f); private static readonly Color PanelColour = new Color(0.05f, 0.05f, 0.06f, 0.78f); private readonly List _sorted = new List(); private GUIStyle _labelStyle; private GUIStyle _listStyle; private Texture2D _panelTexture; public void Draw(IsolationTracker tracker, bool revealOthers) { EnsureStyles(); _sorted.Clear(); if (revealOthers) { SortByHeat(tracker.Watched); if (Settings.ShowLabels.Value) { DrawLabels(); } } if (Settings.ShowCornerList.Value) { DrawCornerList(revealOthers); } if (Settings.ShowOwnScore.Value && tracker.HasLocalScore) { DrawOwnScore(tracker.LocalScore); } } private void DrawLabels() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0188: 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_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) Camera activeCamera = GameAccess.ActiveCamera; if ((Object)(object)activeCamera == (Object)null) { return; } int num = Mathf.Min(_sorted.Count, Mathf.Max(0, Settings.MaxLabels.Value)); Rect val4 = default(Rect); for (int i = 0; i < num; i++) { ScoredPlayer scoredPlayer = _sorted[i]; Vector3 val = activeCamera.WorldToScreenPoint(scoredPlayer.Position + Vector3.up * 2.1f); if (!(val.z <= 0f)) { string text = (scoredPlayer.Flagged ? ("RAMBO: " + Mathf.FloorToInt(scoredPlayer.DwellSeconds) + "s") : "ISOLATED"); string text2 = scoredPlayer.Name + "\n" + text + "\nISO: " + scoredPlayer.Isolation + " DGR: " + scoredPlayer.Danger; GUIContent val2 = new GUIContent(text2); Vector2 val3 = _labelStyle.CalcSize(val2); val3.y = _labelStyle.CalcHeight(val2, val3.x); ((Rect)(ref val4))..ctor(val.x - val3.x * 0.5f, (float)Screen.height - val.y - val3.y, val3.x, val3.y); GUI.DrawTexture(val4, (Texture)(object)_panelTexture); _labelStyle.normal.textColor = (scoredPlayer.Flagged ? FlagColour : WatchColour); GUI.Label(val4, text2, _labelStyle); } } } private void DrawCornerList(bool revealOthers) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: 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_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) Camera activeCamera = GameAccess.ActiveCamera; Vector3 val = (((Object)(object)activeCamera != (Object)null) ? ((Component)activeCamera).transform.position : Vector3.zero); string text = (revealOthers ? ("AdminHelper " + _sorted.Count + " watched, " + CountFlagged() + " flagged") : "AdminHelper admin login required"); float num = 260f; float num2 = 18f; Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(12f, 12f, num, num2 * (float)(_sorted.Count + 1) + 10f); GUI.DrawTexture(val2, (Texture)(object)_panelTexture); _listStyle.normal.textColor = InfoColour; GUI.Label(new Rect(((Rect)(ref val2)).x + 6f, ((Rect)(ref val2)).y + 5f, num - 12f, num2), text, _listStyle); Rect val3 = default(Rect); for (int i = 0; i < _sorted.Count; i++) { ScoredPlayer scoredPlayer = _sorted[i]; float num3 = Horizontal(scoredPlayer.Position - val); ((Rect)(ref val3))..ctor(((Rect)(ref val2)).x + 6f, ((Rect)(ref val2)).y + 5f + num2 * (float)(i + 1), num - 12f, num2); _listStyle.normal.textColor = (scoredPlayer.Flagged ? FlagColour : WatchColour); GUI.Label(val3, scoredPlayer.Isolation + "/" + scoredPlayer.Danger + " " + Mathf.RoundToInt(num3) + "m " + scoredPlayer.Name, _listStyle); } } private void DrawOwnScore(ScoredPlayer local) { //IL_00e8: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) string text = "ISO: " + local.Isolation + " DGR: " + local.Danger + "\nmates " + Metres(local.MateDistance) + " enemy " + Metres(local.EnemyDistance) + " x" + local.EnemyCount + (local.InFormation ? "\nin formation" : "\nout of formation") + " dwell " + local.DwellSeconds.ToString("0.0") + "s"; Rect val = default(Rect); ((Rect)(ref val))..ctor((float)Screen.width - 232f, (float)Screen.height - 76f, 220f, 64f); GUI.DrawTexture(val, (Texture)(object)_panelTexture); _labelStyle.normal.textColor = (local.Flagged ? FlagColour : InfoColour); GUI.Label(val, text, _labelStyle); } private static string Metres(float distance) { if (distance >= 9000f) { return "none"; } return distance.ToString("0.0") + "m"; } private void SortByHeat(List watched) { _sorted.Clear(); _sorted.AddRange(watched); } private int CountFlagged() { int num = 0; for (int i = 0; i < _sorted.Count; i++) { if (_sorted[i].Flagged) { num++; } } return num; } private void EnsureStyles() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0027: 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_0070: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Expected O, but got Unknown //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown //IL_0122: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_panelTexture == (Object)null) { _panelTexture = new Texture2D(1, 1); _panelTexture.SetPixel(0, 0, PanelColour); _panelTexture.Apply(); ((Object)_panelTexture).hideFlags = (HideFlags)61; } if (_labelStyle == null) { _labelStyle = new GUIStyle(GUI.skin.label); _labelStyle.alignment = (TextAnchor)4; _labelStyle.fontSize = 13; _labelStyle.fontStyle = (FontStyle)1; _labelStyle.padding = new RectOffset(6, 6, 3, 3); _labelStyle.normal.textColor = FlagColour; } if (_listStyle == null) { _listStyle = new GUIStyle(GUI.skin.label); _listStyle.alignment = (TextAnchor)3; _listStyle.fontSize = 12; _listStyle.padding = new RectOffset(0, 0, 0, 0); _listStyle.normal.textColor = FlagColour; } } private static float Horizontal(Vector3 delta) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) return Mathf.Sqrt(delta.x * delta.x + delta.z * delta.z); } } internal sealed class IsolationTracker { private sealed class State { public float Isolation; public float Dwell; public int LastSeenTick; } private const float SoloEnemyFloor = 0.5f; private const int MaxWatched = 32; private readonly Dictionary _states = new Dictionary(); private readonly List _players = new List(); private readonly Dictionary> _byFaction = new Dictionary>(); private readonly List _stale = new List(); private int _tick; public readonly List Watched = new List(); public ScoredPlayer LocalScore; public bool HasLocalScore; public void Reset() { _states.Clear(); _byFaction.Clear(); Watched.Clear(); HasLocalScore = false; } public void Tick(float dt) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) _tick++; Watched.Clear(); HasLocalScore = false; GameAccess.CollectPlayers(_players); if (_players.Count == 0) { DropStaleStates(); return; } BucketByFaction(); int localPlayerId = GameAccess.LocalPlayerId; ScoredPlayer scoredPlayer = default(ScoredPlayer); for (int i = 0; i < _players.Count; i++) { PlayerSnapshot self = _players[i]; List list = _byFaction[self.Faction]; if (list.Count >= 3) { float num = MateDistance(self, list); bool flag = FormationDetector.IsInFormation(self, list); float num2 = Curve(num); if (flag) { num2 *= 1f - Mathf.Clamp01(Settings.FormationSuppression.Value); } State state = ResolveState(self.PlayerId); Integrate(state, num2, dt); MeasureEnemies(self, out var nearestDistance, out var countInRadius); int num3 = Mathf.RoundToInt(state.Isolation * 100f); int danger = Mathf.RoundToInt((float)num3 * EnemyThreat(nearestDistance, countInRadius)); bool flag2 = IsScorable(self); if (flag2 && num3 >= Settings.RamboThreshold.Value) { state.Dwell += dt; } else { state.Dwell = Mathf.Max(0f, state.Dwell - dt * Mathf.Max(1f, Settings.RecoverMultiplier.Value)); } scoredPlayer.PlayerId = self.PlayerId; scoredPlayer.Name = self.Name; scoredPlayer.Position = self.Position; scoredPlayer.Isolation = num3; scoredPlayer.Danger = danger; scoredPlayer.DwellSeconds = state.Dwell; scoredPlayer.Flagged = flag2 && state.Dwell >= Settings.RamboHoldSeconds.Value; scoredPlayer.MateDistance = num; scoredPlayer.EnemyDistance = nearestDistance; scoredPlayer.EnemyCount = countInRadius; scoredPlayer.InFormation = flag; if (flag2 && num3 >= Settings.RingThreshold.Value) { Watched.Add(scoredPlayer); } if (self.PlayerId == localPlayerId) { LocalScore = scoredPlayer; HasLocalScore = true; } } } SortAndCapWatched(); DropStaleStates(); } private void SortAndCapWatched() { Watched.Sort((ScoredPlayer a, ScoredPlayer b) => b.Isolation.CompareTo(a.Isolation)); if (Watched.Count > 32) { Watched.RemoveRange(32, Watched.Count - 32); } } private bool IsScorable(PlayerSnapshot self) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (self.IsCavalry && !Settings.ScoreCavalry.Value) { return false; } if (self.IsArtillery) { return false; } return !Settings.IsExempt(self.Class); } private static float Curve(float distance) { float value = Settings.ClusterNearMetres.Value; float num = Mathf.Max(value + 1f, Settings.ClusterFarMetres.Value); return Mathf.Clamp01((distance - value) / (num - value)); } private float MateDistance(PlayerSnapshot self, List friendlies) { //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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) float num = float.MaxValue; float num2 = float.MaxValue; Vector3 val = Vector3.zero; Vector3 val2 = Vector3.zero; for (int i = 0; i < friendlies.Count; i++) { PlayerSnapshot playerSnapshot = friendlies[i]; if (playerSnapshot.PlayerId != self.PlayerId) { float num3 = playerSnapshot.Position.x - self.Position.x; float num4 = playerSnapshot.Position.z - self.Position.z; float num5 = num3 * num3 + num4 * num4; if (num5 < num) { num2 = num; val2 = val; num = num5; val = playerSnapshot.Position; } else if (num5 < num2) { num2 = num5; val2 = playerSnapshot.Position; } } } if (num2 == float.MaxValue) { return float.MaxValue; } float num6 = (val.x + val2.x) * 0.5f; float num7 = (val.z + val2.z) * 0.5f; return Horizontal(self.Position.x - num6, self.Position.z - num7); } private void MeasureEnemies(PlayerSnapshot self, out float nearestDistance, out int countInRadius) { //IL_0031: 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) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_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) float value = Settings.EnemyRadius.Value; float num = value * value; float num2 = float.MaxValue; countInRadius = 0; for (int i = 0; i < _players.Count; i++) { PlayerSnapshot playerSnapshot = _players[i]; if (playerSnapshot.Faction != self.Faction) { float num3 = playerSnapshot.Position.x - self.Position.x; float num4 = playerSnapshot.Position.z - self.Position.z; float num5 = num3 * num3 + num4 * num4; if (num5 < num2) { num2 = num5; } if (num5 <= num) { countInRadius++; } } } nearestDistance = ((num2 == float.MaxValue) ? float.MaxValue : Mathf.Sqrt(num2)); } private static float EnemyThreat(float nearestDistance, int countInRadius) { float value = Settings.EnemyRadius.Value; if (value <= 0f || nearestDistance > value) { return 0f; } float num = Mathf.Clamp01((value - nearestDistance) / value); float num2 = Mathf.Clamp01((float)countInRadius / Mathf.Max(1f, (float)Settings.EnemyCrowd.Value)); return num * (0.5f + 0.5f * num2); } private static void Integrate(State state, float target, float dt) { float num = Mathf.Max(0.05f, Settings.RiseSeconds.Value); float num2 = 1f / num; if (target < state.Isolation) { num2 *= Mathf.Max(1f, Settings.RecoverMultiplier.Value); } state.Isolation += (target - state.Isolation) * (1f - Mathf.Exp((0f - num2) * dt)); } private void BucketByFaction() { //IL_0050: 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_005c: 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) foreach (KeyValuePair> item in _byFaction) { item.Value.Clear(); } for (int i = 0; i < _players.Count; i++) { FactionCountry faction = _players[i].Faction; if (!_byFaction.TryGetValue(faction, out var value)) { value = new List(150); _byFaction[faction] = value; } value.Add(_players[i]); } } private State ResolveState(int playerId) { if (!_states.TryGetValue(playerId, out var value)) { value = new State(); _states[playerId] = value; } value.LastSeenTick = _tick; return value; } private void DropStaleStates() { _stale.Clear(); foreach (KeyValuePair state in _states) { if (_tick - state.Value.LastSeenTick > 60) { _stale.Add(state.Key); } } for (int i = 0; i < _stale.Count; i++) { _states.Remove(_stale[i]); } } private static float Horizontal(float dx, float dz) { return Mathf.Sqrt(dx * dx + dz * dz); } } internal static class Log { private static readonly ManualLogSource Source = Logger.CreateLogSource("AdminHelper"); public static void Info(string message) { Source.LogInfo((object)message); } public static void Warn(string message) { Source.LogWarning((object)message); } public static void Error(string message) { Source.LogError((object)message); } } internal struct PlayerSnapshot { public RoundPlayer Player; public int PlayerId; public string Name; public Vector3 Position; public FactionCountry Faction; public PlayerClass Class; public bool IsCavalry; public bool IsArtillery; } internal struct ScoredPlayer { public int PlayerId; public string Name; public Vector3 Position; public int Isolation; public int Danger; public float DwellSeconds; public bool Flagged; public float MateDistance; public float EnemyDistance; public int EnemyCount; public bool InFormation; } internal sealed class RingRenderer { private const int Segments = 48; private const float Radius = 1.6f; private const float GroundOffset = 0.05f; private readonly List _pool = new List(); private readonly Vector3[] _points = (Vector3[])(object)new Vector3[49]; private GameObject _root; private Shader _shader; public void Draw(List flagged) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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) EnsureRoot(); for (int i = 0; i < flagged.Count; i++) { LineRenderer val = Resolve(i); ((Component)val).gameObject.SetActive(true); Color val2 = ColourFor(flagged[i].Isolation); Color startColor = (val.endColor = val2); val.startColor = startColor; if ((Object)(object)((Renderer)val).sharedMaterial != (Object)null) { ((Renderer)val).sharedMaterial.color = val2; } BuildCircle(flagged[i].Position); val.SetPositions(_points); } for (int j = flagged.Count; j < _pool.Count; j++) { if ((Object)(object)_pool[j] != (Object)null) { ((Component)_pool[j]).gameObject.SetActive(false); } } } public void HideAll() { for (int i = 0; i < _pool.Count; i++) { if ((Object)(object)_pool[i] != (Object)null) { ((Component)_pool[i]).gameObject.SetActive(false); } } } public void Destroy() { _pool.Clear(); if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } _root = null; } private void BuildCircle(Vector3 centre) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) float num = centre.y + 0.05f; for (int i = 0; i <= 48; i++) { float num2 = (float)i * (float)Math.PI * 2f / 48f; _points[i] = new Vector3(centre.x + Mathf.Cos(num2) * 1.6f, num, centre.z + Mathf.Sin(num2) * 1.6f); } } private static Color ColourFor(int isolation) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) float num = Settings.RingThreshold.Value; float num2 = Mathf.Max(num + 1f, (float)Settings.RamboThreshold.Value); float num3 = Mathf.Clamp01(((float)isolation - num) / (num2 - num)); return Color.Lerp(new Color(1f, 0.82f, 0.15f), new Color(1f, 0.25f, 0.1f), num3); } private void EnsureRoot() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if (!((Object)(object)_root != (Object)null)) { _root = new GameObject("AdminHelperRings"); Object.DontDestroyOnLoad((Object)(object)_root); _pool.Clear(); } } private LineRenderer Resolve(int index) { while (_pool.Count <= index) { _pool.Add(Create()); } LineRenderer val = _pool[index]; if ((Object)(object)val == (Object)null) { val = Create(); _pool[index] = val; } return val; } private LineRenderer Create() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown GameObject val = new GameObject("Ring"); val.transform.SetParent(_root.transform, false); LineRenderer val2 = val.AddComponent(); val2.useWorldSpace = true; val2.loop = false; val2.positionCount = 49; float startWidth = (val2.endWidth = 0.14f); val2.startWidth = startWidth; val2.numCapVertices = 0; ((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val2).receiveShadows = false; ((Renderer)val2).sharedMaterial = new Material(ResolveShader()); return val2; } private Shader ResolveShader() { if ((Object)(object)_shader != (Object)null) { return _shader; } _shader = Shader.Find("Universal Render Pipeline/Unlit"); if ((Object)(object)_shader == (Object)null) { _shader = Shader.Find("Sprites/Default"); } if ((Object)(object)_shader == (Object)null) { _shader = Shader.Find("Hidden/Internal-Colored"); } return _shader; } } internal static class Settings { public static ConfigEntry Enabled; public static ConfigEntry TickHz; public static ConfigEntry RequireAdminLogin; public static ConfigEntry ClusterNearMetres; public static ConfigEntry ClusterFarMetres; public static ConfigEntry RiseSeconds; public static ConfigEntry RecoverMultiplier; public static ConfigEntry EnemyRadius; public static ConfigEntry EnemyCrowd; public static ConfigEntry FormationSuppression; public static ConfigEntry LineRadius; public static ConfigEntry LineMinMates; public static ConfigEntry LineMaxMates; public static ConfigEntry LineResidual; public static ConfigEntry ClusterMinMates; public static ConfigEntry ClusterFormationRadius; public static ConfigEntry RingThreshold; public static ConfigEntry RamboThreshold; public static ConfigEntry RamboHoldSeconds; public static ConfigEntry ScoreCavalry; public static ConfigEntry ExemptClasses; public static ConfigEntry ShowRings; public static ConfigEntry ShowLabels; public static ConfigEntry ShowCornerList; public static ConfigEntry ShowOwnScore; public static ConfigEntry MaxLabels; public static ConfigEntry ToggleKey; public static ConfigEntry StartHudVisible; private static readonly HashSet ExemptSet = new HashSet(); private static string _exemptSource; private static string _toggleKeySource; private static KeyCode _toggleKey = (KeyCode)287; private static ConfigFile _config; private static DateTime _stamp; private static float _nextCheck; public static void Create(ConfigFile config) { _config = config; Enabled = config.Bind("General", "Enabled", true, "Master switch. Turning this off stops the scorer as well as the HUD."); TickHz = config.Bind("General", "TickHz", 5f, "Scoring ticks per second. Higher costs more and buys nothing."); RequireAdminLogin = config.Bind("General", "RequireAdminLogin", true, "Only show other players once the server has authenticated an 'rc login'. Turning this off makes the mod a wallhack."); ClusterNearMetres = config.Bind("Isolation", "ClusterNearMetres", 10f, "Distance from the midpoint of your two nearest mates at which isolation starts counting."); ClusterFarMetres = config.Bind("Isolation", "ClusterFarMetres", 30f, "Distance at which isolation is fully saturated."); RiseSeconds = config.Bind("Scoring", "RiseSeconds", 6f, "Time constant for the score climbing. Larger is slower to flag."); RecoverMultiplier = config.Bind("Scoring", "RecoverMultiplier", 3f, "How much faster the score falls than it climbs when a player rejoins."); EnemyRadius = config.Bind("Danger", "EnemyRadius", 30f, "How close an enemy has to be before any isolation counts as danger."); EnemyCrowd = config.Bind("Danger", "EnemyCrowd", 3, "Enemies inside the radius that count as being fully inside their formation."); FormationSuppression = config.Bind("Formation", "FormationSuppression", 0.9f, "Fraction of the raw isolation signal removed while a player is in formation."); LineRadius = config.Bind("Formation", "LineRadius", 10f, "Radius searched for formation mates."); LineMinMates = config.Bind("Formation", "LineMinMates", 2, "Mates needed before the line fit is attempted."); LineMaxMates = config.Bind("Formation", "LineMaxMates", 6, "Most mates fed into the line fit."); LineResidual = config.Bind("Formation", "LineResidual", 2f, "Metres of spread either side of the best-fit line still counted as a line."); ClusterMinMates = config.Bind("Formation", "ClusterMinMates", 3, "Mates within ClusterFormationRadius that count as a square or skirmisher knot."); ClusterFormationRadius = config.Bind("Formation", "ClusterFormationRadius", 8f, "Radius for the tight cluster test."); RingThreshold = config.Bind("Flagging", "RingThreshold", 40, "Isolation score at which a marker appears. Tracks the live score, so it clears as a player returns."); RamboThreshold = config.Bind("Flagging", "RamboThreshold", 75, "Isolation score at which the dwell timer starts running."); RamboHoldSeconds = config.Bind("Flagging", "RamboHoldSeconds", 5f, "Seconds above the threshold before a player is flagged."); ScoreCavalry = config.Bind("Flagging", "ScoreCavalry", false, "Score cavalry too. Off by default since cavalry operating apart is not a rambo."); ExemptClasses = config.Bind("Flagging", "ExemptClasses", "", "Comma-separated PlayerClass names that are never flagged, e.g. Surgeon,Sapper."); ShowRings = config.Bind("Display", "ShowRings", true, "Draw a ground ring under each watched player."); ShowLabels = config.Bind("Display", "ShowLabels", true, "Draw the floating name and score label over each watched player."); ShowCornerList = config.Bind("Display", "ShowCornerList", true, "List watched players in the corner with their scores and distance."); ShowOwnScore = config.Bind("Display", "ShowOwnScore", true, "Your own scores plus the raw distances behind them. The fastest way to pick thresholds."); MaxLabels = config.Bind("Display", "MaxLabels", 12, "Most floating labels drawn at once, nearest first."); ToggleKey = config.Bind("Display", "ToggleKey", "F6", "Key that hides and shows the HUD. Any UnityEngine.KeyCode name."); StartHudVisible = config.Bind("Display", "StartHudVisible", false, "Whether the HUD starts visible when the game launches. After that the toggle sticks until you quit."); _stamp = Stamp(); } public static void PollForExternalEdits() { if (_config != null && !(Time.unscaledTime < _nextCheck)) { _nextCheck = Time.unscaledTime + 1f; DateTime dateTime = Stamp(); if (!(dateTime == _stamp)) { _stamp = dateTime; _config.Reload(); } } } private static DateTime Stamp() { try { return File.GetLastWriteTimeUtc(_config.ConfigFilePath); } catch (Exception) { return _stamp; } } public static bool IsExempt(PlayerClass playerClass) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) RefreshExemptSet(); return ExemptSet.Contains(playerClass); } private static void RefreshExemptSet() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) string text = ExemptClasses.Value ?? string.Empty; if (text == _exemptSource) { return; } _exemptSource = text; ExemptSet.Clear(); string[] array = text.Split(','); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length != 0) { try { ExemptSet.Add((PlayerClass)Enum.Parse(typeof(PlayerClass), text2, ignoreCase: true)); } catch (Exception) { Log.Warn("Unknown PlayerClass in ExemptClasses: " + text2); } } } } public static KeyCode ResolveToggleKey() { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: 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) //IL_0024: 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_009d: 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_0057: 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_0072: Unknown result type (might be due to invalid IL or missing references) string text = ToggleKey.Value ?? string.Empty; if (text == _toggleKeySource) { return _toggleKey; } _toggleKeySource = text; _toggleKey = (KeyCode)287; string text2 = text.Trim(); if (text2.Length == 0) { return _toggleKey; } try { _toggleKey = (KeyCode)Enum.Parse(typeof(KeyCode), text2, ignoreCase: true); } catch (Exception) { Log.Warn("ToggleKey '" + text2 + "' is not a KeyCode name. Falling back to F6."); } return _toggleKey; } } }