using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Mirror; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("EventChatNotifications")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("EventChatNotifications")] [assembly: AssemblyTitle("EventChatNotifications")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 EventChatNotifications { [BepInPlugin("sbg.eventchatnotifications", "EventChatNotifications", "0.4.1")] public sealed class Plugin : BaseUnityPlugin { public const string ModGuid = "sbg.eventchatnotifications"; public const string ModName = "EventChatNotifications"; public const string ModVersion = "0.4.1"; internal static ManualLogSource Log; private void Awake() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; new Harmony("sbg.eventchatnotifications").PatchAll(); FirstPlaceTracker.Hook(); Log.LogInfo((object)"EventChatNotifications v0.4.1 loaded."); } private void OnDestroy() { FirstPlaceTracker.Unhook(); } } internal static class FirstPlaceTracker { private enum Metric { BestHoleScore, LongestChipIn, AvgFinishTime, ItemPickups, KORatio } private static readonly Metric[] AllMetrics = new Metric[5] { Metric.BestHoleScore, Metric.LongestChipIn, Metric.AvgFinishTime, Metric.ItemPickups, Metric.KORatio }; private static readonly Dictionary CurrentLeader = new Dictionary(); private static bool subscribed; internal static void Hook() { if (!subscribed) { CourseManager.PlayerStatesChanged += OnPlayerStatesChanged; subscribed = true; } } internal static void Unhook() { if (subscribed) { CourseManager.PlayerStatesChanged -= OnPlayerStatesChanged; CurrentLeader.Clear(); subscribed = false; } } private static void OnPlayerStatesChanged(Operation op, int index, PlayerState changed) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) SyncList playerStates = CourseManager.PlayerStates; if (playerStates == null) { return; } int num = 0; for (int i = 0; i < playerStates.Count; i++) { if (!playerStates[i].isSpectator && playerStates[i].isConnected) { num++; } } if (num >= 2) { Metric[] allMetrics = AllMetrics; foreach (Metric metric in allMetrics) { Reconcile(playerStates, metric); } } } private static void Reconcile(SyncList states, Metric metric) { if (TryComputeLeader(states, metric, out var leaderGuid, out var valueLabel)) { ulong value; bool flag = CurrentLeader.TryGetValue(metric, out value); if (!flag || value != leaderGuid) { CurrentLeader[metric] = leaderGuid; string text = Narrator.PlayerNameFromGuid(leaderGuid); string text2 = MetricLabel(metric); Narrator.Post(flag ? (text + " took first on " + text2 + " from " + Narrator.PlayerNameFromGuid(value) + " — " + valueLabel + ".") : (text + " took first on " + text2 + " — " + valueLabel + ".")); } } } private static bool TryComputeLeader(SyncList states, Metric metric, out ulong leaderGuid, out string valueLabel) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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_00f2: 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) leaderGuid = 0uL; valueLabel = null; bool flag = false; ulong num = 0uL; int num2 = int.MaxValue; float num3 = 0f; int num4 = 0; for (int i = 0; i < states.Count; i++) { PlayerState val = states[i]; if (val.isSpectator || !val.isConnected || !IsEligible(val, metric)) { continue; } bool flag2; if (!flag) { flag2 = true; } else if (UsesIntComparison(metric)) { int num5 = ReadInt(val, metric); flag2 = (LowerIsBetter(metric) ? (num5 < num4) : (num5 > num4)); } else { float num6 = ReadFloat(val, metric); flag2 = (LowerIsBetter(metric) ? (num6 < num3) : (num6 > num3)); } if (flag2) { num = val.playerGuid; num2 = val.joinIndex; flag = true; if (UsesIntComparison(metric)) { num4 = ReadInt(val, metric); } else { num3 = ReadFloat(val, metric); } } else if (HasTie(val, metric, num4, num3) && val.joinIndex < num2) { num = val.playerGuid; num2 = val.joinIndex; } } if (!flag) { return false; } leaderGuid = num; valueLabel = FormatValue(states, num, metric, num4, num3); return true; } private static bool IsEligible(PlayerState s, Metric metric) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_0026: 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: 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_0041: Unknown result type (might be due to invalid IL or missing references) switch (metric) { case Metric.BestHoleScore: return (int)s.bestHoleScore > 0; case Metric.LongestChipIn: return s.longestChipIn > float.MinValue; case Metric.AvgFinishTime: if (s.avgFinishTime > 0f) { return s.finishes > 0; } return false; case Metric.ItemPickups: return s.itemPickups > 0; case Metric.KORatio: return s.matchKnockouts > 0; default: return false; } } private static bool UsesIntComparison(Metric m) { if (m != 0) { return m == Metric.ItemPickups; } return true; } private static bool LowerIsBetter(Metric m) { return m == Metric.AvgFinishTime; } private static int ReadInt(PlayerState s, Metric m) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected I4, but got Unknown //IL_0010: Unknown result type (might be due to invalid IL or missing references) return m switch { Metric.BestHoleScore => (int)s.bestHoleScore, Metric.ItemPickups => s.itemPickups, _ => 0, }; } private static float ReadFloat(PlayerState s, Metric m) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) return m switch { Metric.LongestChipIn => s.longestChipIn, Metric.AvgFinishTime => s.avgFinishTime, Metric.KORatio => ComputeKORatio(s), _ => 0f, }; } private static float ComputeKORatio(PlayerState s) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Max(1, s.matchKnockedOut); return (float)s.matchKnockouts / (float)num; } private static bool HasTie(PlayerState s, Metric metric, int bestInt, float bestFloat) { //IL_0013: 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) if (UsesIntComparison(metric)) { return ReadInt(s, metric) == bestInt; } return Mathf.Approximately(ReadFloat(s, metric), bestFloat); } private static string FormatValue(SyncList states, ulong leaderGuid, Metric metric, int bestInt, float bestFloat) { //IL_005d: 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_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_0089: Unknown result type (might be due to invalid IL or missing references) switch (metric) { case Metric.BestHoleScore: return DescribeStrokesUnderPar((StrokesUnderParType)bestInt); case Metric.LongestChipIn: return $"{Mathf.RoundToInt(bestFloat)}m"; case Metric.AvgFinishTime: return $"{bestFloat:0.0}s"; case Metric.ItemPickups: return bestInt.ToString(); case Metric.KORatio: { for (int i = 0; i < states.Count; i++) { if (states[i].playerGuid == leaderGuid) { PlayerState val = states[i]; return $"{bestFloat:0.00} ({val.matchKnockouts}/{val.matchKnockedOut})"; } } return bestFloat.ToString("0.00"); } default: return string.Empty; } } private static string MetricLabel(Metric m) { return m switch { Metric.BestHoleScore => "best hole", Metric.LongestChipIn => "longest chip-in", Metric.AvgFinishTime => "average finish time", Metric.ItemPickups => "item pickups", Metric.KORatio => "K/O ratio", _ => "stat", }; } private static string DescribeStrokesUnderPar(StrokesUnderParType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected I4, but got Unknown return (type - 1) switch { 5 => "Hole in One", 4 => "Condor", 3 => "Albatross", 2 => "Eagle", 1 => "Birdie", 0 => "Par", _ => "—", }; } } internal static class Narrator { private const string Prefix = "[Match] "; internal static void Post(string message) { if (string.IsNullOrEmpty(message)) { return; } try { TextChatUi.ShowMessage("[Match] " + message); } catch { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[chat-fallback] " + message)); } } } internal static string PlayerNameFromGuid(ulong guid) { PlayerInfo localPlayerInfo = GameManager.LocalPlayerInfo; if ((Object)(object)localPlayerInfo != (Object)null && (Object)(object)localPlayerInfo.PlayerId != (Object)null && localPlayerInfo.PlayerId.Guid == guid) { return localPlayerInfo.PlayerId.PlayerNameNoRichText; } if (GameManager.RemotePlayers != null) { foreach (PlayerInfo remotePlayer in GameManager.RemotePlayers) { if (!((Object)(object)remotePlayer == (Object)null) && !((Object)(object)remotePlayer.PlayerId == (Object)null) && remotePlayer.PlayerId.Guid == guid) { return remotePlayer.PlayerId.PlayerNameNoRichText; } } } return "Someone"; } internal static string DescribeStrokesUnderPar(StrokesUnderParType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected I4, but got Unknown return (type - 2) switch { 4 => "scored a hole in one!", 2 => "landed an albatross!", 1 => "landed an eagle!", 0 => "landed a birdie!", 3 => "pulled off a condor!", _ => null, }; } } [HarmonyPatch(typeof(InfoFeed), "UserCode_RpcShowMessage__StrokesMessageData")] internal static class Patch_StrokesMessage { private static void Postfix(StrokesMessageData messageData) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: 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) string text = Narrator.DescribeStrokesUnderPar(messageData.strokesUnderParType); if (text != null) { Narrator.Post(Narrator.PlayerNameFromGuid(messageData.playerGuid) + " " + text); } } } [HarmonyPatch(typeof(InfoFeed), "UserCode_RpcShowMessage__SpeedrunMessageData")] internal static class Patch_SpeedrunMessage { private static void Postfix(SpeedrunMessageData messageData) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Narrator.Post(Narrator.PlayerNameFromGuid(messageData.playerGuid) + " sprinted through the hole — that was fast!"); } } }