using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using Agents; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using ChainedPuzzles; using Enemies; using GTFO.API; using GameData; using Gear; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using KainAdvancedScoreboard.Core; using KainAdvancedScoreboard.Localization; using KainAdvancedScoreboard.Network; using KainAdvancedScoreboard.Patches; using KainAdvancedScoreboard.Runtime; using KainAdvancedScoreboard.UI; using LevelGeneration; using Microsoft.CodeAnalysis; using Player; using SNetwork; using UnityEngine; 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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [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 KainAdvancedScoreboard.UI { public sealed class ScoreboardPanel { private sealed class ScoreRow { public string Label => ScoreboardLocalization.Pick(EnglishLabel, ChineseLabel); private string EnglishLabel { get; } private string ChineseLabel { get; } public Func Value { get; } public Func? Highlight { get; } public ScoreRow(string englishLabel, string chineseLabel, Func value, Func? highlight) { EnglishLabel = englishLabel; ChineseLabel = chineseLabel; Value = value; Highlight = highlight; } } private const int MaxPlayerColumns = 4; private const float HeaderHeight = 64f; private const float RowHeight = 30f; private const float HeaderRowHeight = 42f; private readonly Func _snapshotProvider; private readonly List _highlightTexts = new List(); private readonly List _highlightBackplates = new List(); private readonly List _scanLineLayers = new List(); private GameObject? _root; private RectTransform? _content; private RectTransform? _scanLine; private Image? _frameImage; private Outline? _frameOutline; private Text? _title; private Text? _subtitle; private Font? _font; private ScoreboardSnapshot _snapshot = ScoreboardSnapshot.Empty; private float _visibleSince; public bool Visible { get { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown if ((Object)_root != (Object)null) { return _root.activeSelf; } return false; } } public ScoreboardPanel(Func snapshotProvider) { _snapshotProvider = snapshotProvider; } public void Create() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0118: 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_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: 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) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_043a: Unknown result type (might be due to invalid IL or missing references) //IL_044a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)_root != (Object)null)) { _font = Resources.GetBuiltinResource("Arial.ttf"); _root = new GameObject("KAS_ScoreboardCanvas"); Object.DontDestroyOnLoad((Object)_root); Canvas obj = _root.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 6000; CanvasScaler obj2 = _root.AddComponent(); obj2.uiScaleMode = (ScaleMode)1; obj2.referenceResolution = new Vector2(2560f, 1440f); obj2.matchWidthOrHeight = 0.5f; _root.AddComponent(); GameObject obj3 = CreateRect("Dim", _root.transform); Stretch(obj3, 0f, 0f, 0f, 0f); ((Graphic)obj3.AddComponent()).color = new Color(0f, 0f, 0f, 0.18f); GameObject val = CreateRect("Frame", _root.transform); RectTransform component = val.GetComponent(); component.anchorMin = new Vector2(0.03f, 0.06f); component.anchorMax = new Vector2(0.97f, 0.94f); component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; _frameImage = val.AddComponent(); ((Graphic)_frameImage).color = new Color(0f, 0f, 0f, 0.34f); _frameOutline = val.AddComponent(); ((Shadow)_frameOutline).effectColor = new Color(0.78f, 0.8f, 0.82f, 0.2f); ((Shadow)_frameOutline).effectDistance = new Vector2(2f, -2f); CreateFrameDecorations(val.transform); _title = CreateText("Title", val.transform, string.Empty, 30, (TextAnchor)4, new Color(1f, 0.88f, 0.48f, 1f)); SetRect(((Component)_title).gameObject, 24f, -50f, 24f, 8f, new Vector2(0f, 1f), new Vector2(1f, 1f)); AddShadow(_title, new Color(0f, 0f, 0f, 0.85f), new Vector2(2f, -2f)); _subtitle = CreateText("Subtitle", val.transform, string.Empty, 15, (TextAnchor)5, new Color(0.78f, 0.8f, 0.82f, 0.92f)); SetRect(((Component)_subtitle).gameObject, 44f, -68f, 44f, 42f, new Vector2(0f, 1f), new Vector2(1f, 1f)); GameObject obj4 = CreateRect("HeaderLine", val.transform); SetRect(obj4, 44f, -74f, 44f, 71f, new Vector2(0f, 1f), new Vector2(1f, 1f)); ((Graphic)obj4.AddComponent()).color = new Color(0.82f, 0.84f, 0.86f, 0.14f); GameObject val2 = CreateRect("Viewport", val.transform); SetRect(val2, 40f, 34f, 40f, 82f, Vector2.zero, Vector2.one); ((Graphic)val2.AddComponent()).color = new Color(0f, 0f, 0f, 0.12f); val2.AddComponent().showMaskGraphic = false; GameObject val3 = CreateRect("Content", val2.transform); _content = val3.GetComponent(); _content.anchorMin = new Vector2(0f, 1f); _content.anchorMax = new Vector2(1f, 1f); _content.pivot = new Vector2(0.5f, 1f); _content.offsetMin = Vector2.zero; _content.offsetMax = Vector2.zero; ScrollRect obj5 = val2.AddComponent(); obj5.content = _content; obj5.viewport = val2.GetComponent(); obj5.horizontal = false; obj5.vertical = true; obj5.movementType = (MovementType)2; obj5.scrollSensitivity = 34f; CreateViewportDecorations(val2.transform); _root.SetActive(false); } } public void Show(ScoreboardSnapshot snapshot) { Create(); _visibleSince = Time.unscaledTime; _snapshot = snapshot.Clone(); Rebuild(); GameObject root = _root; if ((Object)(object)root != (Object)null) { root.SetActive(true); } } public void Hide() { GameObject root = _root; if ((Object)(object)root != (Object)null) { root.SetActive(false); } } public void Refresh(ScoreboardSnapshot snapshot) { _snapshot = snapshot.Clone(); if (Visible) { Rebuild(); } } public void Tick() { if (Input.GetKeyDown((KeyCode)27)) { Hide(); return; } ScoreboardSnapshot scoreboardSnapshot = _snapshotProvider(); if (scoreboardSnapshot.Timestamp != _snapshot.Timestamp || scoreboardSnapshot.Players.Count != _snapshot.Players.Count || scoreboardSnapshot.IsHostAuthoritative != _snapshot.IsHostAuthoritative) { Refresh(scoreboardSnapshot); } Animate(Time.unscaledTime); } private void Rebuild() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) if ((Object)_content == (Object)null) { return; } ClearContent(); _highlightTexts.Clear(); _highlightBackplates.Clear(); if ((Object)_title != (Object)null) { _title.text = ScoreboardLocalization.Title; } if ((Object)_subtitle != (Object)null) { _subtitle.text = "Kain的进阶计分板 Kain Advanced Scoreboard"; } List list = ((_snapshot.Players.Count == 0) ? new List { new PlayerScoreStats { PlayerName = ScoreboardLocalization.NoStats, PlayerSlot = 0 } } : (from player in _snapshot.Players orderby (player.PlayerSlot < 0) ? int.MaxValue : player.PlayerSlot, player.PlayerId select player).Take(4).ToList()); IReadOnlyList readOnlyList = BuildRows(_snapshot.HasObjectivePickups); float num = 42f + (float)readOnlyList.Count * 30f + 10f; _content.sizeDelta = new Vector2(0f, num); int num2 = Mathf.Max(1, list.Count); float centerWidthPixels = 260f; float sideGapPixels = 14f; float playerWidth = (1f - CenterWidthRatio(centerWidthPixels, num2, sideGapPixels)) / (float)num2; float num3 = 0.5f - CenterWidthRatio(centerWidthPixels, num2, sideGapPixels) * 0.5f; float num4 = 0.5f + CenterWidthRatio(centerWidthPixels, num2, sideGapPixels) * 0.5f; CreateCenterHeader(num3, num4); for (int num5 = 0; num5 < list.Count; num5++) { (float, float) tuple = CalculatePlayerColumnAnchor(num5, num2, playerWidth, num3, num4); CreatePlayerHeader(list[num5], tuple.Item1, tuple.Item2); } for (int num6 = 0; num6 < readOnlyList.Count; num6++) { float yTop = -42f - (float)num6 * 30f; CreateMetricCell(readOnlyList[num6].Label, num3, num4, yTop, center: true); for (int num7 = 0; num7 < list.Count; num7++) { (float, float) tuple2 = CalculatePlayerColumnAnchor(num7, num2, playerWidth, num3, num4); string value = readOnlyList[num6].Value(list[num7]); CreateValueCell(value, tuple2.Item1, tuple2.Item2, yTop, IsHighlighted(readOnlyList[num6], list[num7])); } } } private static float CenterWidthRatio(float centerWidthPixels, int playerCount, float sideGapPixels) { float num = 2200f; float num2 = ((playerCount <= 2) ? 0.22f : 0.18f); return Mathf.Clamp((centerWidthPixels + sideGapPixels * 2f) / num, num2, 0.26f); } private static (float Min, float Max) CalculatePlayerColumnAnchor(int playerIndex, int playerCount, float playerWidth, float centerMin, float centerMax) { int num = Mathf.CeilToInt((float)playerCount / 2f); if (playerIndex < num) { float num2 = centerMin / (float)num; return (Min: (float)playerIndex * num2 + 0.006f, Max: (float)(playerIndex + 1) * num2 - 0.006f); } int num3 = playerCount - num; if (num3 <= 0) { return (Min: 0.74f, Max: 0.99f); } int num4 = playerIndex - num; float num5 = (1f - centerMax) / (float)num3; return (Min: centerMax + (float)num4 * num5 + 0.006f, Max: centerMax + (float)(num4 + 1) * num5 - 0.006f); } private void CreateCenterHeader(float anchorMinX, float anchorMaxX) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown //IL_0042: 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_0093: 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_00df: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateRect("MetricHeader", (Transform)_content); SetCellRect(val, anchorMinX, anchorMaxX, 0f, 42f); ((Graphic)val.AddComponent()).color = new Color(0f, 0f, 0f, 0.42f); AddInsetOutline(val, new Color(0.76f, 0.78f, 0.8f, 0.14f)); Text obj = CreateText("MetricHeaderText", val.transform, ScoreboardLocalization.Title, 16, (TextAnchor)4, new Color(1f, 0.88f, 0.55f, 1f)); Stretch(((Component)obj).gameObject, 4f, 0f, 4f, 0f); AddShadow(obj, new Color(0f, 0f, 0f, 0.85f), new Vector2(1f, -1f)); } private void CreatePlayerHeader(PlayerScoreStats player, float anchorMinX, float anchorMaxX) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown //IL_0042: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateRect("PlayerHeader", (Transform)_content); SetCellRect(val, anchorMinX, anchorMaxX, 0f, 42f); ((Graphic)val.AddComponent()).color = new Color(0f, 0f, 0f, 0.38f); AddInsetOutline(val, new Color(0.76f, 0.78f, 0.8f, 0.12f)); string text = (string.IsNullOrWhiteSpace(player.PlayerName) ? "Player" : player.PlayerName); Text obj = CreateText("PlayerHeaderText", val.transform, text, 16, (TextAnchor)4, Color.white); Stretch(((Component)obj).gameObject, 8f, 0f, 8f, 0f); AddShadow(obj, new Color(0f, 0f, 0f, 0.9f), new Vector2(1f, -1f)); } private void CreateMetricCell(string label, float anchorMinX, float anchorMaxX, float yTop, bool center) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown //IL_003f: 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_00c1: 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) GameObject val = CreateRect("MetricCell", (Transform)_content); SetCellRect(val, anchorMinX, anchorMaxX, yTop, 30f); ((Graphic)val.AddComponent()).color = new Color(0f, 0f, 0f, 0.44f); AddCellDivider(val.transform); Text obj = CreateText("MetricText", val.transform, ScoreboardLocalization.Upper(label), 14, (TextAnchor)(center ? 4 : 3), new Color(0.88f, 0.9f, 0.82f, 1f)); Stretch(((Component)obj).gameObject, 8f, 0f, 8f, 0f); AddShadow(obj, new Color(0f, 0f, 0f, 0.85f), new Vector2(1f, -1f)); } private void CreateValueCell(string value, float anchorMinX, float anchorMaxX, float yTop, bool highlighted) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown //IL_0060: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) GameObject val = CreateRect("ValueCell", (Transform)_content); SetCellRect(val, anchorMinX, anchorMaxX, yTop, 30f); Image val2 = val.AddComponent(); ((Graphic)val2).color = (highlighted ? new Color(0.08f, 0.075f, 0.055f, 0.44f) : new Color(0f, 0f, 0f, 0.28f)); AddCellDivider(val.transform); if (highlighted) { _highlightBackplates.Add(val2); GameObject obj = CreateRect("HighlightStrip", val.transform); SetRect(obj, 0f, 0f, 0f, 0f, new Vector2(0f, 0f), new Vector2(0f, 1f)); obj.GetComponent().sizeDelta = new Vector2(3f, 0f); ((Graphic)obj.AddComponent()).color = new Color(1f, 0.76f, 0.32f, 0.72f); } Color color = (highlighted ? new Color(1f, 0.84f, 0.44f, 1f) : new Color(0.86f, 0.87f, 0.84f, 1f)); Text val3 = CreateText("ValueText", val.transform, value, 16, (TextAnchor)4, color); Stretch(((Component)val3).gameObject, 4f, 0f, 4f, 0f); AddShadow(val3, new Color(0f, 0f, 0f, 0.88f), new Vector2(1f, -1f)); if (highlighted) { _highlightTexts.Add(val3); } } private static bool IsHighlighted(ScoreRow row, PlayerScoreStats player) { if (row.Highlight != null) { return row.Highlight(player); } return false; } private void CreateFrameDecorations(Transform frame) { //IL_002a: 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_005c: 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_0095: 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) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: 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_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) GameObject obj = CreateRect("TopGlow", frame); SetRect(obj, 42f, -6f, 42f, 0f, new Vector2(0f, 1f), new Vector2(1f, 1f)); ((Graphic)obj.AddComponent()).color = new Color(0.9f, 0.92f, 0.94f, 0.08f); GameObject obj2 = CreateRect("BottomGlow", frame); SetRect(obj2, 42f, 0f, 42f, 6f, Vector2.zero, new Vector2(1f, 0f)); ((Graphic)obj2.AddComponent()).color = new Color(0.9f, 0.92f, 0.94f, 0.05f); CreateCorner(frame, "TopLeftCorner", 28f, -28f, new Vector2(0f, 1f), left: true, top: true); CreateCorner(frame, "TopRightCorner", -28f, -28f, new Vector2(1f, 1f), left: false, top: true); CreateCorner(frame, "BottomLeftCorner", 28f, 28f, new Vector2(0f, 0f), left: true, top: false); CreateCorner(frame, "BottomRightCorner", -28f, 28f, new Vector2(1f, 0f), left: false, top: false); for (int i = 0; i < 7; i++) { GameObject obj3 = CreateRect("TopTick" + i.ToString(CultureInfo.InvariantCulture), frame); float num = 0.17f + (float)i * 0.11f; obj3.GetComponent().anchorMin = new Vector2(num, 1f); obj3.GetComponent().anchorMax = new Vector2(num, 1f); obj3.GetComponent().pivot = new Vector2(0.5f, 1f); obj3.GetComponent().sizeDelta = new Vector2(34f, 4f); obj3.GetComponent().anchoredPosition = new Vector2(0f, -18f); ((Graphic)obj3.AddComponent()).color = new Color(0.86f, 0.88f, 0.9f, 0.12f); } } private void CreateViewportDecorations(Transform viewport) { //IL_0026: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) _scanLine = CreateRect("ScanLine", viewport).GetComponent(); _scanLine.anchorMin = new Vector2(0f, 1f); _scanLine.anchorMax = new Vector2(1f, 1f); _scanLine.pivot = new Vector2(0.5f, 1f); _scanLine.offsetMin = new Vector2(0f, -7f); _scanLine.offsetMax = new Vector2(0f, 0f); _scanLineLayers.Clear(); CreateScanLineLayer((Transform)_scanLine, "ScanLineDeep", 7f, new Color(0.02f, 0.08f, 0.32f, 0.12f)); CreateScanLineLayer((Transform)_scanLine, "ScanLineMid", 3f, new Color(0.05f, 0.26f, 0.86f, 0.2f)); CreateScanLineLayer((Transform)_scanLine, "ScanLineCore", 1f, new Color(0.32f, 0.7f, 1f, 0.42f)); for (int i = 1; i < 8; i++) { GameObject obj = CreateRect("VerticalGuide" + i.ToString(CultureInfo.InvariantCulture), viewport); RectTransform component = obj.GetComponent(); float num = (float)i / 8f; component.anchorMin = new Vector2(num, 0f); component.anchorMax = new Vector2(num, 1f); component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(1f, 0f); ((Graphic)obj.AddComponent()).color = new Color(0.82f, 0.84f, 0.86f, 0.035f); } } private void CreateScanLineLayer(Transform parent, string name, float height, Color color) { //IL_0018: 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_0042: 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_0075: 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) GameObject obj = CreateRect(name, parent); RectTransform component = obj.GetComponent(); component.anchorMin = new Vector2(0f, 0.5f); component.anchorMax = new Vector2(1f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.offsetMin = new Vector2(0f, (0f - height) * 0.5f); component.offsetMax = new Vector2(0f, height * 0.5f); Image val = obj.AddComponent(); ((Graphic)val).color = color; _scanLineLayers.Add(val); } private static void CreateCorner(Transform parent, string name, float x, float y, Vector2 anchor, bool left, bool top) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) GameObject obj = CreateRect(name + "H", parent); RectTransform component = obj.GetComponent(); component.anchorMin = anchor; component.anchorMax = anchor; component.pivot = new Vector2(left ? 0f : 1f, top ? 1f : 0f); component.sizeDelta = new Vector2(96f, 3f); component.anchoredPosition = new Vector2(x, y); ((Graphic)obj.AddComponent()).color = new Color(0.86f, 0.88f, 0.9f, 0.22f); GameObject obj2 = CreateRect(name + "V", parent); RectTransform component2 = obj2.GetComponent(); component2.anchorMin = anchor; component2.anchorMax = anchor; component2.pivot = new Vector2(left ? 0f : 1f, top ? 1f : 0f); component2.sizeDelta = new Vector2(3f, 66f); component2.anchoredPosition = new Vector2(x, y); ((Graphic)obj2.AddComponent()).color = new Color(0.86f, 0.88f, 0.9f, 0.22f); } private static void AddCellDivider(Transform parent) { //IL_0020: 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_0049: 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) GameObject obj = CreateRect("Divider", parent); SetRect(obj, 0f, 0f, 0f, 0f, Vector2.zero, new Vector2(1f, 0f)); obj.GetComponent().sizeDelta = new Vector2(0f, 1f); ((Graphic)obj.AddComponent()).color = new Color(0.82f, 0.84f, 0.86f, 0.08f); } private static void AddInsetOutline(GameObject go, Color color) { //IL_0007: 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) Outline obj = go.AddComponent(); ((Shadow)obj).effectColor = color; ((Shadow)obj).effectDistance = new Vector2(1f, -1f); } private void Animate(float now) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_0057: 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: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Expected O, but got Unknown //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Expected O, but got Unknown //IL_0291: Unknown result type (might be due to invalid IL or missing references) float num = now - _visibleSince; float num2 = 0.5f + 0.5f * Mathf.Sin(num * 2.15f); if ((Object)_frameImage != (Object)null) { ((Graphic)_frameImage).color = new Color(0f, 0f, 0f, 0.3f + num2 * 0.06f); } if ((Object)_frameOutline != (Object)null) { ((Shadow)_frameOutline).effectColor = new Color(0.78f, 0.8f, 0.82f, 0.12f + num2 * 0.12f); } if ((Object)_scanLine != (Object)null) { float num3 = Mathf.PingPong(num * 58f, 560f); _scanLine.anchoredPosition = new Vector2(0f, 0f - num3); } for (int i = 0; i < _scanLineLayers.Count; i++) { if (!((Object)_scanLineLayers[i] == (Object)null)) { switch (i) { case 0: ((Graphic)_scanLineLayers[i]).color = new Color(0.02f, 0.08f, 0.32f, 0.08f + num2 * 0.08f); break; case 1: ((Graphic)_scanLineLayers[i]).color = new Color(0.05f, 0.26f, 0.86f, 0.12f + num2 * 0.12f); break; default: ((Graphic)_scanLineLayers[i]).color = new Color(0.32f, 0.7f, 1f, 0.24f + num2 * 0.22f); break; } } } Color color = default(Color); ((Color)(ref color))..ctor(1f, 0.84f, 0.44f, 0.84f + num2 * 0.16f); for (int j = 0; j < _highlightTexts.Count; j++) { if ((Object)_highlightTexts[j] != (Object)null) { ((Graphic)_highlightTexts[j]).color = color; } } Color color2 = default(Color); ((Color)(ref color2))..ctor(0.08f + num2 * 0.015f, 0.075f + num2 * 0.012f, 0.055f, 0.28f + num2 * 0.12f); for (int k = 0; k < _highlightBackplates.Count; k++) { if ((Object)_highlightBackplates[k] != (Object)null) { ((Graphic)_highlightBackplates[k]).color = color2; } } } private static IReadOnlyList BuildRows(bool showObjectivePickups) { return new List { new ScoreRow("total kills", "总击杀", (PlayerScoreStats p) => (p.SmallKills + p.LargeKills + p.ScoutKills + p.BossKills).ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.SmallKills + p.LargeKills + p.ScoutKills + p.BossKills > 0), new ScoreRow("small kills", "小型击杀", (PlayerScoreStats p) => p.SmallKills.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.SmallKills > 0), new ScoreRow("large kills", "大型击杀", (PlayerScoreStats p) => p.LargeKills.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.LargeKills > 0), new ScoreRow("scouts killed", "Scout 击杀", (PlayerScoreStats p) => p.ScoutKills.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.ScoutKills > 0), new ScoreRow("bosses killed", "Boss 击杀", (PlayerScoreStats p) => p.BossKills.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.BossKills > 0), new ScoreRow("melee kills", "近战击杀", (PlayerScoreStats p) => p.MeleeKills.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.MeleeKills > 0), new ScoreRow("tool kills", "工具击杀", (PlayerScoreStats p) => p.ToolKills.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.ToolKills > 0), new ScoreRow("stealth kills", "潜行击杀", (PlayerScoreStats p) => p.StealthKills.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.StealthKills > 0), new ScoreRow("damage dealt", "总伤害", (PlayerScoreStats p) => FormatNumber(p.TotalDamage), (PlayerScoreStats p) => p.TotalDamage > 0f), new ScoreRow("damage taken", "承受伤害", (PlayerScoreStats p) => FormatNumber(p.DamageTaken), (PlayerScoreStats p) => p.DamageTaken > 0f), new ScoreRow("primary shots", "主武器开火", (PlayerScoreStats p) => p.PrimaryWeapon.ShotsFired.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.PrimaryWeapon.ShotsFired > 0), new ScoreRow("primary hits", "主武器命中", (PlayerScoreStats p) => p.PrimaryWeapon.Hits.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.PrimaryWeapon.Hits > 0), new ScoreRow("primary weak hits", "主武器弱点", (PlayerScoreStats p) => p.PrimaryWeapon.WeakspotHits.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.PrimaryWeapon.WeakspotHits > 0), new ScoreRow("primary accuracy", "主武器命中率", (PlayerScoreStats p) => FormatPercent(p.PrimaryWeapon.HitRate), (PlayerScoreStats p) => p.PrimaryWeapon.HitRate > 0f), new ScoreRow("primary weak rate", "主武器弱点率", (PlayerScoreStats p) => FormatPercent(p.PrimaryWeapon.WeakspotRate), (PlayerScoreStats p) => p.PrimaryWeapon.WeakspotRate > 0f), new ScoreRow("secondary shots", "副武器开火", (PlayerScoreStats p) => p.SecondaryWeapon.ShotsFired.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.SecondaryWeapon.ShotsFired > 0), new ScoreRow("secondary hits", "副武器命中", (PlayerScoreStats p) => p.SecondaryWeapon.Hits.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.SecondaryWeapon.Hits > 0), new ScoreRow("secondary weak hits", "副武器弱点", (PlayerScoreStats p) => p.SecondaryWeapon.WeakspotHits.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.SecondaryWeapon.WeakspotHits > 0), new ScoreRow("secondary accuracy", "副武器命中率", (PlayerScoreStats p) => FormatPercent(p.SecondaryWeapon.HitRate), (PlayerScoreStats p) => p.SecondaryWeapon.HitRate > 0f), new ScoreRow("secondary weak rate", "副武器弱点率", (PlayerScoreStats p) => FormatPercent(p.SecondaryWeapon.WeakspotRate), (PlayerScoreStats p) => p.SecondaryWeapon.WeakspotRate > 0f), new ScoreRow("downs", "倒地次数", (PlayerScoreStats p) => p.DownedCount.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.DownedCount > 0), new ScoreRow("rescues", "救援次数", (PlayerScoreStats p) => p.RescueCount.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.RescueCount > 0), new ScoreRow("med packs received", "医疗包次数", (PlayerScoreStats p) => p.MedicalPacksReceived.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.MedicalPacksReceived > 0), new ScoreRow("ammo packs received", "弹药包次数", (PlayerScoreStats p) => p.AmmoPacksReceived.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.AmmoPacksReceived > 0), new ScoreRow("tool packs received", "工具包次数", (PlayerScoreStats p) => p.ToolPacksReceived.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.ToolPacksReceived > 0), new ScoreRow("disinfect packs received", "消毒包次数", (PlayerScoreStats p) => p.DisinfectionPacksReceived.ToString(CultureInfo.InvariantCulture), (PlayerScoreStats p) => p.DisinfectionPacksReceived > 0), new ScoreRow("damage per ammo pack", "每份弹药包伤害", (PlayerScoreStats p) => float.IsPositiveInfinity(p.DamagePerAmmoPack) ? ScoreboardLocalization.Pick("Infinity", "无穷大") : FormatNumber(p.DamagePerAmmoPack), (PlayerScoreStats p) => !float.IsPositiveInfinity(p.DamagePerAmmoPack) && p.DamagePerAmmoPack > 0f), new ScoreRow("scan participation", "扫描参与时间", (PlayerScoreStats p) => FormatSeconds(p.ScanParticipationSeconds), (PlayerScoreStats p) => p.ScanParticipationSeconds > 0f) }; } private void ClearContent() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown if (!((Object)_content == (Object)null)) { for (int num = ((Transform)_content).childCount - 1; num >= 0; num--) { Object.Destroy((Object)((Component)((Transform)_content).GetChild(num)).gameObject); } } } private Text CreateText(string name, Transform parent, string text, int fontSize, TextAnchor alignment, Color color) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) Text obj = CreateRect(name, parent).AddComponent(); obj.font = _font ?? Resources.GetBuiltinResource("Arial.ttf"); obj.fontSize = fontSize; obj.text = text; obj.alignment = alignment; ((Graphic)obj).color = color; obj.horizontalOverflow = (HorizontalWrapMode)0; obj.verticalOverflow = (VerticalWrapMode)0; obj.supportRichText = false; ((Graphic)obj).raycastTarget = false; return obj; } private static GameObject CreateRect(string name, Transform parent) { //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_001b: Expected O, but got Unknown GameObject val = new GameObject(name); val.transform.SetParent(parent, false); val.AddComponent(); return val; } private static void Stretch(GameObject go, float left, float bottom, float right, float top) { //IL_0007: 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_001f: 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) RectTransform component = go.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = new Vector2(left, bottom); component.offsetMax = new Vector2(0f - right, 0f - top); } private static void SetRect(GameObject go, float left, float bottom, float right, float top, Vector2 anchorMin, Vector2 anchorMax) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) RectTransform component = go.GetComponent(); component.anchorMin = anchorMin; component.anchorMax = anchorMax; component.offsetMin = new Vector2(left, bottom); component.offsetMax = new Vector2(0f - right, 0f - top); } private static void SetCellRect(GameObject go, float anchorMinX, float anchorMaxX, float yTop, float height) { //IL_000d: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) RectTransform component = go.GetComponent(); component.anchorMin = new Vector2(anchorMinX, 1f); component.anchorMax = new Vector2(anchorMaxX, 1f); component.pivot = new Vector2(0.5f, 1f); component.offsetMin = new Vector2(0f, yTop - height); component.offsetMax = new Vector2(0f, yTop); } private static void AddShadow(Text text, Color color, Vector2 distance) { //IL_000c: 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) Shadow obj = ((Component)text).gameObject.AddComponent(); obj.effectColor = color; obj.effectDistance = distance; } private static string FormatNumber(float value) { if (!(value >= 100000f)) { if (!(value >= 1000f)) { return value.ToString("0.#", CultureInfo.InvariantCulture); } return value.ToString("0", CultureInfo.InvariantCulture); } return (value / 1000f).ToString("0.#", CultureInfo.InvariantCulture) + "K"; } private static string FormatPercent(float value) { return (value * 100f).ToString("0.#", CultureInfo.InvariantCulture) + "%"; } private static string FormatSeconds(float value) { return value.ToString("0.0", CultureInfo.InvariantCulture) + "s"; } } } namespace KainAdvancedScoreboard.Runtime { public enum DamageSourceType { Unknown, PrimaryWeapon, SecondaryWeapon, Melee, Tool } public sealed class EnemyAttribution { public ulong LastDamageDealer { get; set; } public float LastDamageTime { get; set; } public DamageSourceType LastDamageSource { get; set; } public bool LastDamageWasWeakspot { get; set; } public bool LastDamageWasMelee { get; set; } public bool LastDamageWasSleepingTarget { get; set; } public ulong LastTaggedBy { get; set; } public float LastTaggedTime { get; set; } public ulong LastFoamedBy { get; set; } public float LastFoamedTime { get; set; } public float LastKnownHealthMax { get; set; } public uint EnemyDataId { get; set; } public bool IsScout { get; set; } public ulong LastCountedDamageDealer { get; set; } public float LastCountedDamageTime { get; set; } public float LastCountedDamageAmount { get; set; } } public sealed class PlayerScoreStats { public ulong PlayerId { get; set; } public string PlayerName { get; set; } = string.Empty; public int PlayerSlot { get; set; } = -1; public ulong SteamId { get; set; } public int SmallKills { get; set; } public int LargeKills { get; set; } public int ScoutKills { get; set; } public int BossKills { get; set; } public float TotalDamage { get; set; } public WeaponScoreStats PrimaryWeapon { get; set; } = new WeaponScoreStats(); public WeaponScoreStats SecondaryWeapon { get; set; } = new WeaponScoreStats(); public float DamageTaken { get; set; } public int DownedCount { get; set; } public int RescueCount { get; set; } public int MedicalPacksReceived { get; set; } public int AmmoPacksReceived { get; set; } public int ToolPacksReceived { get; set; } public int DisinfectionPacksReceived { get; set; } public int ObjectivePickups { get; set; } public int TerminalCommands { get; set; } public int MeleeKills { get; set; } public int ToolKills { get; set; } public int BioTrackerTaggedEnemies { get; set; } public float ScanParticipationSeconds { get; set; } public int StealthKills { get; set; } public float DamagePerAmmoPack { get { if (AmmoPacksReceived > 0) { return TotalDamage / (float)AmmoPacksReceived; } return float.PositiveInfinity; } } public PlayerScoreStats Clone() { return new PlayerScoreStats { PlayerId = PlayerId, PlayerName = PlayerName, PlayerSlot = PlayerSlot, SteamId = SteamId, SmallKills = SmallKills, LargeKills = LargeKills, ScoutKills = ScoutKills, BossKills = BossKills, TotalDamage = TotalDamage, PrimaryWeapon = PrimaryWeapon.Clone(), SecondaryWeapon = SecondaryWeapon.Clone(), DamageTaken = DamageTaken, DownedCount = DownedCount, RescueCount = RescueCount, MedicalPacksReceived = MedicalPacksReceived, AmmoPacksReceived = AmmoPacksReceived, ToolPacksReceived = ToolPacksReceived, DisinfectionPacksReceived = DisinfectionPacksReceived, ObjectivePickups = ObjectivePickups, TerminalCommands = TerminalCommands, MeleeKills = MeleeKills, ToolKills = ToolKills, BioTrackerTaggedEnemies = BioTrackerTaggedEnemies, ScanParticipationSeconds = ScanParticipationSeconds, StealthKills = StealthKills }; } public void MergeMax(PlayerScoreStats other) { if (string.IsNullOrWhiteSpace(PlayerName)) { PlayerName = other.PlayerName; } if (PlayerSlot < 0) { PlayerSlot = other.PlayerSlot; } if (SteamId == 0L) { SteamId = other.SteamId; } SmallKills = Math.Max(SmallKills, other.SmallKills); LargeKills = Math.Max(LargeKills, other.LargeKills); ScoutKills = Math.Max(ScoutKills, other.ScoutKills); BossKills = Math.Max(BossKills, other.BossKills); TotalDamage = Math.Max(TotalDamage, other.TotalDamage); PrimaryWeapon.MergeMax(other.PrimaryWeapon); SecondaryWeapon.MergeMax(other.SecondaryWeapon); DamageTaken = Math.Max(DamageTaken, other.DamageTaken); DownedCount = Math.Max(DownedCount, other.DownedCount); RescueCount = Math.Max(RescueCount, other.RescueCount); MedicalPacksReceived = Math.Max(MedicalPacksReceived, other.MedicalPacksReceived); AmmoPacksReceived = Math.Max(AmmoPacksReceived, other.AmmoPacksReceived); ToolPacksReceived = Math.Max(ToolPacksReceived, other.ToolPacksReceived); DisinfectionPacksReceived = Math.Max(DisinfectionPacksReceived, other.DisinfectionPacksReceived); ObjectivePickups = Math.Max(ObjectivePickups, other.ObjectivePickups); TerminalCommands = Math.Max(TerminalCommands, other.TerminalCommands); MeleeKills = Math.Max(MeleeKills, other.MeleeKills); ToolKills = Math.Max(ToolKills, other.ToolKills); BioTrackerTaggedEnemies = Math.Max(BioTrackerTaggedEnemies, other.BioTrackerTaggedEnemies); ScanParticipationSeconds = Math.Max(ScanParticipationSeconds, other.ScanParticipationSeconds); StealthKills = Math.Max(StealthKills, other.StealthKills); } } public enum ResourcePackKind { Medical, Ammo, Tool, Disinfection } public sealed class ScoreboardNativeCallbacks { private readonly ScoreboardRuntime _runtime; private bool _installed; private Action? _enemyDamaged; private Action? _enemyKilled; private Action? _playerDamaged; private Action? _playerDowned; private Action? _playerShotWeapon; private Action? _playerActivatedTool; private Action? _playerUsedFlashlight; private Action? _playerUsedResource; private Action? _playerUsedDisinfectionStation; private Action? _playerTeleported; private Action? _expeditionComplete; private Action? _readLog; public ScoreboardNativeCallbacks(ScoreboardRuntime runtime) { _runtime = runtime; } public void Install() { if (!_installed) { _enemyDamaged = _runtime.OnNativeEnemyDamaged; _enemyKilled = _runtime.OnNativeEnemyKilled; _playerDamaged = _runtime.OnNativePlayerDamaged; _playerDowned = _runtime.OnNativePlayerDowned; _playerShotWeapon = _runtime.OnNativePlayerShotWeapon; _playerActivatedTool = _runtime.OnNativePlayerActivatedTool; _playerUsedFlashlight = _runtime.OnNativePlayerUsedFlashlight; _playerUsedResource = _runtime.OnNativePlayerUsedResource; _playerUsedDisinfectionStation = _runtime.OnNativePlayerUsedDisinfectionStation; _playerTeleported = _runtime.OnNativePlayerTeleported; _expeditionComplete = _runtime.OnNativeExpeditionComplete; _readLog = _runtime.OnNativeReadLog; GlobalCallbacks.OnEnemyDamagedMaster += Action.op_Implicit(_enemyDamaged); GlobalCallbacks.OnEnemyKilledMaster += Action.op_Implicit(_enemyKilled); GlobalCallbacks.OnPlayerDamaged += Action.op_Implicit(_playerDamaged); GlobalCallbacks.OnPlayerDowned += Action.op_Implicit(_playerDowned); GlobalCallbacks.OnPlayerShotWeapon += Action.op_Implicit(_playerShotWeapon); GlobalCallbacks.OnPlayerActivatedTool += Action.op_Implicit(_playerActivatedTool); GlobalCallbacks.OnPlayerUsedFlashlight += Action.op_Implicit(_playerUsedFlashlight); GlobalCallbacks.OnPlayerUsedResource += Action.op_Implicit(_playerUsedResource); GlobalCallbacks.OnPlayerUsedDisinfectionStation += Action.op_Implicit(_playerUsedDisinfectionStation); GlobalCallbacks.OnPlayerTeleported += Action.op_Implicit(_playerTeleported); GlobalCallbacks.OnExpeditionComplete += Action.op_Implicit(_expeditionComplete); GlobalCallbacks.OnReadLog += Action.op_Implicit(_readLog); _installed = true; } } } public sealed class ScoreboardRuntime { private readonly Func _verboseEventLog; private readonly Dictionary _statsByPlayer = new Dictionary(); private readonly Dictionary _playersByAgentId = new Dictionary(); private readonly Dictionary _playersByUnityId = new Dictionary(); private readonly Dictionary _enemyAttribution = new Dictionary(); private readonly Dictionary _reviveTargetsByInteraction = new Dictionary(); private readonly Dictionary _downStateByPlayer = new Dictionary(); private readonly Dictionary _nextBioscanSampleByCore = new Dictionary(); private readonly Dictionary _remoteShotBufferByPlayer = new Dictionary(); private readonly Dictionary _peerSnapshotSeenAtByPlayer = new Dictionary(); private readonly Dictionary _lastDamageTakenAt = new Dictionary(); private readonly Dictionary _lastDamageTakenAmount = new Dictionary(); private readonly Dictionary _lastResourceReceivedAt = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _lastReviveCompletedAt = new Dictionary(StringComparer.Ordinal); private readonly HashSet _countedTerminalCommands = new HashSet(); private readonly HashSet _countedObjectivePickups = new HashSet(); private readonly HashSet _countedBioTags = new HashSet(StringComparer.Ordinal); private ScoreboardSnapshot _hostSnapshot = ScoreboardSnapshot.Empty.Clone(); private bool _expeditionEnded; private bool _hasObjectivePickups; private ulong _localPlayerId; private float _nextLifeStateSampleAt; public bool IsHostAuthority { get { try { return !SNet.IsOnline || !SNet.HasMaster || SNet.IsMaster; } catch { return true; } } } public ScoreboardRuntime(Func verboseEventLog) { _verboseEventLog = verboseEventLog; } public void ResetForNewExpedition() { _statsByPlayer.Clear(); _playersByAgentId.Clear(); _playersByUnityId.Clear(); _enemyAttribution.Clear(); _reviveTargetsByInteraction.Clear(); _downStateByPlayer.Clear(); _nextBioscanSampleByCore.Clear(); _remoteShotBufferByPlayer.Clear(); _peerSnapshotSeenAtByPlayer.Clear(); _lastDamageTakenAt.Clear(); _lastDamageTakenAmount.Clear(); _lastResourceReceivedAt.Clear(); _lastReviveCompletedAt.Clear(); _countedTerminalCommands.Clear(); _countedObjectivePickups.Clear(); _countedBioTags.Clear(); _hostSnapshot = ScoreboardSnapshot.Empty.Clone(); _expeditionEnded = false; _hasObjectivePickups = false; _localPlayerId = 0uL; _nextLifeStateSampleAt = 0f; TryRegisterLocalPlayer(); } public void MarkExpeditionEnded() { _expeditionEnded = true; } public void SamplePlayerLifeStates() { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown float time = Time.time; if (time < _nextLifeStateSampleAt) { return; } _nextLifeStateSampleAt = time + 0.25f; TryRegisterLocalPlayer(); TryRegisterPlayersInSlots(); HashSet hashSet = new HashSet(); foreach (PlayerAgent item in new List(_playersByUnityId.Values)) { if ((Object)item == (Object)null || !ShouldCollectSharedMetric(item)) { continue; } RegisterPlayer(item); ulong playerId = GetPlayerId(item); if (!hashSet.Add(playerId)) { continue; } bool flag = IsPlayerDown(item); if (!_downStateByPlayer.TryGetValue(playerId, out var value)) { _downStateByPlayer[playerId] = flag; if (flag) { GetOrCreateStats(playerId).DownedCount++; } } else { if (flag && !value) { GetOrCreateStats(playerId).DownedCount++; } _downStateByPlayer[playerId] = flag; } } } public void RegisterPlayer(PlayerAgent? player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)player == (Object)null)) { ulong playerId = GetPlayerId(player); if (IsLocalHumanPlayer(player)) { _localPlayerId = playerId; } PlayerScoreStats orCreateStats = GetOrCreateStats(playerId); orCreateStats.PlayerSlot = SafePlayerSlot(player); orCreateStats.PlayerName = SafePlayerName(player, orCreateStats.PlayerName); orCreateStats.SteamId = SafeSteamId(player); if (((Agent)player).GameObjectID > 0) { _playersByAgentId[((Agent)player).GameObjectID] = player; } int instanceID = ((Object)player).GetInstanceID(); if (instanceID != 0) { _playersByUnityId[instanceID] = player; } } } public void OnBulletWeaponFired(BulletWeapon? weapon) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown if (!((Object)weapon == (Object)null)) { PlayerAgent val = ResolvePlayerFromObject(weapon); if ((Object)val == (Object)null) { val = TryGetLocalPlayer(); } if (!((Object)val == (Object)null) && ShouldCollectPeerOwnedMetric(val)) { RegisterPlayer(val); CountWeaponShots(GetPlayerId(val), ResolveWeaponSlot(weapon), ResolveProjectileCountPerShot(val)); } } } public void OnEnemyDamaged(Dam_EnemyDamageBase? damageBase, Agent? sourceAgent, float damage, int limbId, bool isMelee, bool wasSleepingTarget) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)damageBase == (Object)null)) { CountReceivedEnemyDamage(damageBase, sourceAgent, damage, limbId, isMelee ? DamageSourceType.Melee : DamageSourceType.Unknown, isMelee, wasSleepingTarget); } } public void OnEnemyBulletDamageReceived(Dam_EnemyDamageBase? damageBase, pBulletDamageData data) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0010: 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_002d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)damageBase == (Object)null)) { CountReceivedEnemyDamage(damageBase, ResolveAgent(data.source), ((UFloat16)(ref data.damage)).Get(((Dam_SyncedDamageBase)damageBase).HealthMax), data.limbID, DamageSourceType.Unknown, isMelee: false, wasSleepingTarget: false); } } public void OnEnemyMeleeDamageReceived(Dam_EnemyDamageBase? damageBase, pFullDamageData data) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0010: 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_002d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)damageBase == (Object)null)) { CountReceivedEnemyDamage(damageBase, ResolveAgent(data.source), ((UFloat16)(ref data.damage)).Get(((Dam_SyncedDamageBase)damageBase).HealthMax), data.limbID, DamageSourceType.Melee, isMelee: true, wasSleepingTarget: false); } } public void OnEnemyTakeDamage(EnemyAgent? enemy, float damage, Agent? sourceAgent) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if (!((Object)enemy == (Object)null) && !((Object)enemy.Damage == (Object)null)) { CountReceivedEnemyDamage(enemy.Damage, sourceAgent, damage, -1, ResolveDamageSourceType(ResolvePlayer(sourceAgent), sourceAgent), isMelee: false, IsEnemySleeping(enemy)); } } public void OnSyncedBulletsFired(PlayerSync? sync, int count) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown if (!((Object)sync == (Object)null) && count > 0) { PlayerAgent val = ResolvePlayerFromObject(sync); if ((Object)val == (Object)null) { val = TryGetLocalPlayer(); } if (!((Object)val == (Object)null) && ShouldCollectPeerOwnedMetric(val)) { RegisterPlayer(val); CountWeaponShots(GetPlayerId(val), ResolveWeaponSlot(val), AdjustSyncedShotCountForWeapon(val, count)); } } } public void OnPlayerInventorySyncedGetSync(PlayerInventorySynced? inventory) { } public void OnEnemyDead(EnemyAgent? enemy) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)enemy == (Object)null)) { IntPtr enemyKey = GetEnemyKey(enemy, enemy.Damage); if (enemyKey != IntPtr.Zero && _enemyAttribution.TryGetValue(enemyKey, out EnemyAttribution value)) { CountKillFromAttribution(enemy, enemy.Damage, value); _enemyAttribution.Remove(enemyKey); } } } public void OnEnemyTagged(EnemyAgent? enemy) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown if ((Object)enemy == (Object)null) { return; } PlayerAgent val = TryGetLocalPlayer(); if ((Object)val == (Object)null) { val = TryGetSingleKnownPlayer(); } if (!((Object)val == (Object)null)) { RegisterPlayer(val); ulong playerId = GetPlayerId(val); IntPtr enemyKey = GetEnemyKey(enemy, enemy.Damage); if (enemyKey != IntPtr.Zero) { EnemyAttribution orCreateAttribution = GetOrCreateAttribution(enemyKey); orCreateAttribution.LastTaggedBy = playerId; orCreateAttribution.LastTaggedTime = Time.time; } string item = playerId.ToString(CultureInfo.InvariantCulture) + ":" + enemyKey.ToInt64().ToString(CultureInfo.InvariantCulture); if (_countedBioTags.Add(item)) { GetOrCreateStats(playerId).BioTrackerTaggedEnemies++; } } } public void OnPlayerDamage(PlayerAgent? player, float damage, string reason) { } public void OnIncomingPlayerDamage(PlayerAgent? player, float damage, float originalDamage, Agent? sourceAgent) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)player == (Object)null) && ShouldCollectSharedMetric(player)) { float amount = Mathf.Max(damage, originalDamage); CountDamageTaken(player, amount); } } public void OnResourceReceived(PlayerAgent? receiver, PlayerAgent? giver, ResourcePackKind kind) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown if ((Object)receiver == (Object)null || !ShouldCollectSharedMetric(receiver)) { return; } RegisterPlayer(receiver); if ((Object)giver != (Object)null) { RegisterPlayer(giver); } PlayerScoreStats orCreateStats = GetOrCreateStats(GetPlayerId(receiver)); ulong num = (((Object)giver != (Object)null) ? GetPlayerId(giver) : 0); string key = GetPlayerId(receiver).ToString(CultureInfo.InvariantCulture) + ":" + num.ToString(CultureInfo.InvariantCulture) + ":" + kind; float time = Time.time; if (!_lastResourceReceivedAt.TryGetValue(key, out var value) || !(time - value <= 0.1f)) { _lastResourceReceivedAt[key] = time; switch (kind) { case ResourcePackKind.Medical: orCreateStats.MedicalPacksReceived++; break; case ResourcePackKind.Ammo: orCreateStats.AmmoPacksReceived++; break; case ResourcePackKind.Tool: orCreateStats.ToolPacksReceived++; break; case ResourcePackKind.Disinfection: orCreateStats.DisinfectionPacksReceived++; break; } } } public void OnNativeEnemyDamaged(EnemyDamageInfo? info) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown if (info == null || info.Damage <= 0f) { return; } EnemyAgent damagedAgent = info.DamagedAgent; Dam_EnemyDamageBase val = (((Object)(object)damagedAgent != (Object)null) ? damagedAgent.Damage : null); PlayerAgent val2 = ResolvePlayer(info.Source) ?? ResolvePlayerFromObject(info.Source); ulong num = (((Object)val2 != (Object)null) ? GetPlayerId(val2) : 0); DamageSourceType damageSourceType = ResolveNativeDamageSourceType(val2, info.Source, info.gearCategoryId); IntPtr enemyKey = GetEnemyKey(damagedAgent, val); EnemyAttribution enemyAttribution = ((enemyKey != IntPtr.Zero) ? GetOrCreateAttribution(enemyKey) : null); bool flag = (Object)val != (Object)null && IsWeakspotLimb(val, info.LimbID); bool flag2 = AchievementManager.IsMelee(info.gearCategoryId); bool flag3 = num != 0L && ShouldCollectSharedMetric(val2); if (flag3) { RegisterPlayer(val2); CountEnemyDamage(enemyAttribution, num, info.Damage, damageSourceType, flag, flag2); } if (enemyAttribution != null) { if (flag3) { enemyAttribution.LastDamageDealer = num; enemyAttribution.LastDamageTime = Time.time; enemyAttribution.LastDamageSource = damageSourceType; enemyAttribution.LastDamageWasWeakspot = flag; enemyAttribution.LastDamageWasMelee = flag2; enemyAttribution.LastDamageWasSleepingTarget = IsEnemySleeping(damagedAgent); } enemyAttribution.LastKnownHealthMax = Mathf.Max(enemyAttribution.LastKnownHealthMax, GetEnemyMaxHealth(damagedAgent, val)); enemyAttribution.EnemyDataId = SafeEnemyDataId(damagedAgent); enemyAttribution.IsScout |= IsScout(damagedAgent); } } public void OnNativeEnemyKilled(EnemyDamageInfo? info) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown if (info == null) { return; } EnemyAgent damagedAgent = info.DamagedAgent; Dam_EnemyDamageBase damageBase = (((Object)(object)damagedAgent != (Object)null) ? damagedAgent.Damage : null); PlayerAgent val = ResolvePlayer(info.Source) ?? ResolvePlayerFromObject(info.Source); ulong num = (((Object)val != (Object)null) ? GetPlayerId(val) : 0); DamageSourceType damageSourceType = ResolveNativeDamageSourceType(val, info.Source, info.gearCategoryId); bool flag = AchievementManager.IsMelee(info.gearCategoryId); IntPtr enemyKey = GetEnemyKey(damagedAgent, damageBase); if (num == 0L && enemyKey != IntPtr.Zero && _enemyAttribution.TryGetValue(enemyKey, out EnemyAttribution value)) { num = value.LastDamageDealer; damageSourceType = value.LastDamageSource; flag = value.LastDamageWasMelee; } try { if (num != 0L && (Object)val != (Object)null && ShouldCollectSharedMetric(val)) { RegisterPlayer(val); PlayerScoreStats orCreateStats = GetOrCreateStats(num); float enemyMaxHealth = GetEnemyMaxHealth(damagedAgent, damageBase); if (IsScout(damagedAgent)) { orCreateStats.ScoutKills++; } else if (enemyMaxHealth >= 300f) { orCreateStats.BossKills++; } else if (enemyMaxHealth >= 100f && enemyMaxHealth < 300f) { orCreateStats.LargeKills++; } else { orCreateStats.SmallKills++; } if (flag || damageSourceType == DamageSourceType.Melee) { orCreateStats.MeleeKills++; } if (damageSourceType == DamageSourceType.Tool) { orCreateStats.ToolKills++; } if ((enemyKey != IntPtr.Zero && _enemyAttribution.TryGetValue(enemyKey, out EnemyAttribution value2) && value2.LastDamageWasSleepingTarget) || IsEnemySleeping(damagedAgent)) { orCreateStats.StealthKills++; } } } finally { if (enemyKey != IntPtr.Zero) { _enemyAttribution.Remove(enemyKey); } } } public void OnNativePlayerDamaged(PlayerDamageInfo? info) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if (!((Object)((info != null) ? info.DamagedAgent : null) == (Object)null) && !(info.Damage <= 0f) && ShouldCollectSharedMetric(info.DamagedAgent)) { CountDamageTaken(info.DamagedAgent, info.Damage); } } public void OnNativePlayerDowned(PlayerAgent? player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)player == (Object)null) && ShouldCollectSharedMetric(player)) { RegisterPlayer(player); ulong playerId = GetPlayerId(player); GetOrCreateStats(playerId).DownedCount++; _downStateByPlayer[playerId] = true; } } public void OnNativePlayerShotWeapon(PlayerAgent? player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)player == (Object)null) && ShouldCollectPeerOwnedMetric(player)) { RegisterPlayer(player); CountWeaponShots(GetPlayerId(player), ResolveWeaponSlot(player), ResolveProjectileCountPerShot(player)); } } public void OnNativePlayerActivatedTool(PlayerAgent? player, InventorySlot slot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!((Object)player == (Object)null)) { RegisterPlayer(player); Verbose($"Tool activated. Player={GetPlayerId(player)} Slot={slot}"); } } public void OnNativePlayerUsedFlashlight(PlayerAgent? player, bool enabled) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)player == (Object)null)) { RegisterPlayer(player); Verbose($"Flashlight used. Player={GetPlayerId(player)} Enabled={enabled}"); } } public void OnNativePlayerUsedResource(PlayerAgent? giver, iResourcePackReceiver? receiver, eResourceContainerSpawnType type) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0023: 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_003f: Expected O, but got Unknown PlayerAgent val = ((receiver != null) ? ((Il2CppObjectBase)receiver).TryCast() : null); if ((Object)val == (Object)null) { val = ResolvePlayerFromObject(receiver); } ResourcePackKind? resourcePackKind = ResolveResourcePackKind(type); if (resourcePackKind.HasValue && !((Object)val == (Object)null)) { OnResourceReceived(val, giver, resourcePackKind.Value); } } public void OnNativePlayerUsedDisinfectionStation() { Verbose("Disinfection station used."); } public void OnNativePlayerTeleported(PlayerAgent? player, eDimensionIndex from, eDimensionIndex to) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)player == (Object)null)) { RegisterPlayer(player); Verbose($"Player teleported. Player={GetPlayerId(player)} From={from} To={to}"); } } public void OnNativeExpeditionComplete(RundownDataBlock rundownData, eRundownTier rundownTier, int expeditionIndex, ExpeditionEndState expeditionEndState) { MarkExpeditionEnded(); } public void OnNativeReadLog(pLogRead log) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) Verbose($"Log read. ID={log.ID}"); } public void OnReviveSetup(Interact_Revive? interaction, PlayerAgent? owner) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown if (!((Object)interaction == (Object)null) && !((Object)owner == (Object)null)) { RegisterPlayer(owner); _reviveTargetsByInteraction[((Object)interaction).GetInstanceID()] = owner; } } public void OnReviveCompleted(Interact_Revive? interaction, PlayerAgent? source) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown if ((Object)interaction == (Object)null || (Object)source == (Object)null || !ShouldCollectSharedMetric(source)) { return; } string key = ((Object)interaction).GetInstanceID().ToString(CultureInfo.InvariantCulture) + ":" + GetPlayerId(source).ToString(CultureInfo.InvariantCulture); float time = Time.time; if (!_lastReviveCompletedAt.TryGetValue(key, out var value) || !(time - value <= 0.5f)) { _lastReviveCompletedAt[key] = time; RegisterPlayer(source); GetOrCreateStats(GetPlayerId(source)).RescueCount++; PlayerAgent val = ResolveReviveTarget(interaction, source); if ((Object)val != (Object)null) { RegisterPlayer(val); _downStateByPlayer[GetPlayerId(val)] = false; } } } public void OnBioscanUpdated(CP_Bioscan_Core? core) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown if ((Object)core == (Object)null) { return; } int instanceID = ((Object)core).GetInstanceID(); float time = Time.time; if (_nextBioscanSampleByCore.TryGetValue(instanceID, out var value) && time < value) { return; } _nextBioscanSampleByCore[instanceID] = time + 0.25f; try { List currentBioscanPlayers = GetCurrentBioscanPlayers(core); if (currentBioscanPlayers.Count <= 0) { return; } for (int i = 0; i < currentBioscanPlayers.Count; i++) { PlayerAgent val = currentBioscanPlayers[i]; if (!((Object)val == (Object)null) && ShouldCollectSharedMetric(val)) { RegisterPlayer(val); GetOrCreateStats(GetPlayerId(val)).ScanParticipationSeconds += 0.25f; } } } catch (Exception ex) { ScoreboardLogger.Error("KAS bioscan sample failed.", ex); } } public void OnTerminalCommand(object? terminalOrInterpreter) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown PlayerAgent val = TryGetLocalPlayer() ?? TryGetSingleKnownPlayer(); if (!((Object)val == (Object)null) && ShouldCollectLocalMetric(val)) { Object val2 = (Object)((terminalOrInterpreter is Object) ? terminalOrInterpreter : null); int item = ((val2 != (Object)null) ? (val2.GetInstanceID() ^ Mathf.RoundToInt(Time.time * 2f)) : Mathf.RoundToInt(Time.time * 2f)); if (_countedTerminalCommands.Add(item)) { RegisterPlayer(val); GetOrCreateStats(GetPlayerId(val)).TerminalCommands++; } } } public void OnObjectivePickup(object? pickup, PlayerAgent? player) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown if (!IsObjectivePickup(pickup)) { return; } if ((Object)(object)player == (Object)null) { player = TryGetLocalPlayer(); } if (!((Object)player == (Object)null) && ShouldCollectSharedMetric(player)) { Object val = (Object)((pickup is Object) ? pickup : null); int item = ((val != (Object)null) ? val.GetInstanceID() : (((Object)player).GetInstanceID() ^ Mathf.RoundToInt(Time.time * 10f))); if (_countedObjectivePickups.Add(item)) { _hasObjectivePickups = true; RegisterPlayer(player); GetOrCreateStats(GetPlayerId(player)).ObjectivePickups++; } } } public ScoreboardSnapshot CreateLocalSnapshot() { TryRegisterLocalPlayer(); TryRegisterPlayersInSlots(); return new ScoreboardSnapshot { PluginVersion = "0.1.3", IsHostAuthoritative = IsHostAuthority, ExpeditionEnded = _expeditionEnded, HasObjectivePickups = _hasObjectivePickups, Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), Players = OrderedPlayerStats() }; } public ScoreboardSnapshot CreatePeerSnapshot() { TryRegisterLocalPlayer(); return new ScoreboardSnapshot { PluginVersion = "0.1.3", IsHostAuthoritative = false, ExpeditionEnded = _expeditionEnded, HasObjectivePickups = _hasObjectivePickups, Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), Players = OrderedLocalPlayerStats() }; } public ScoreboardSnapshot CreateDisplaySnapshot() { ScoreboardSnapshot scoreboardSnapshot = CreateLocalSnapshot(); if (!IsHostAuthority && _hostSnapshot.Players.Count > 0) { ScoreboardSnapshot scoreboardSnapshot2 = _hostSnapshot.Clone(); scoreboardSnapshot2.MergeMax(scoreboardSnapshot); return scoreboardSnapshot2; } return scoreboardSnapshot; } public void ApplyHostSnapshot(ScoreboardSnapshot snapshot) { _hostSnapshot = snapshot.Clone(); } public void MergePeerSnapshot(ScoreboardSnapshot snapshot) { if (!IsHostAuthority) { return; } foreach (PlayerScoreStats player in snapshot.Players) { MarkPeerSnapshotSeen(player.PlayerId); GetOrCreateStats(player.PlayerId).MergeMax(player); } _hasObjectivePickups |= snapshot.HasObjectivePickups; _expeditionEnded |= snapshot.ExpeditionEnded; } private void MarkPeerSnapshotSeen(ulong playerId) { if (playerId != 0L) { _peerSnapshotSeenAtByPlayer[playerId] = Time.time; } } private bool IsPeerSnapshotFresh(ulong playerId) { if (playerId != 0L && _peerSnapshotSeenAtByPlayer.TryGetValue(playerId, out var value)) { return Time.time - value <= 8f; } return false; } private List OrderedPlayerStats() { return (from player in new List(_statsByPlayer.Values).ConvertAll((PlayerScoreStats player) => player.Clone()).FindAll(ShouldExportPlayer) orderby (player.PlayerSlot < 0) ? int.MaxValue : player.PlayerSlot, player.PlayerId select player).ToList(); } private List OrderedLocalPlayerStats() { ulong localPlayerId = GetLocalPlayerId(); if (localPlayerId == 0L) { return new List(); } return (from player in new List(_statsByPlayer.Values).ConvertAll((PlayerScoreStats player) => player.Clone()).FindAll((PlayerScoreStats player) => player.PlayerId == localPlayerId && ShouldExportPlayer(player)) orderby (player.PlayerSlot < 0) ? int.MaxValue : player.PlayerSlot, player.PlayerId select player).ToList(); } private PlayerScoreStats GetOrCreateStats(ulong playerId) { if (playerId == 0L) { playerId = 999999uL; } if (_statsByPlayer.TryGetValue(playerId, out PlayerScoreStats value)) { return value; } value = new PlayerScoreStats { PlayerId = playerId, PlayerName = "Player " + playerId.ToString(CultureInfo.InvariantCulture) }; _statsByPlayer[playerId] = value; return value; } private EnemyAttribution GetOrCreateAttribution(IntPtr key) { if (_enemyAttribution.TryGetValue(key, out EnemyAttribution value)) { return value; } value = new EnemyAttribution(); _enemyAttribution[key] = value; return value; } private void TryRegisterLocalPlayer() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown PlayerAgent val = TryGetLocalPlayer(); if ((Object)val != (Object)null) { RegisterPlayer(val); } } private void TryRegisterPlayersInSlots() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown try { List playerAgentsInLevel = PlayerManager.PlayerAgentsInLevel; if (playerAgentsInLevel == null) { return; } for (int i = 0; i < playerAgentsInLevel.Count; i++) { PlayerAgent val = playerAgentsInLevel[i]; if ((Object)val != (Object)null && SafePlayerSlot(val) >= 0) { RegisterPlayer(val); } } } catch { } } private PlayerAgent? TryGetLocalPlayer() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown try { PlayerAgent val = null; if (PlayerManager.TryGetLocalPlayerAgent(ref val) && (Object)val != (Object)null) { return val; } } catch { } return null; } private ulong GetLocalPlayerId() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown if (_localPlayerId != 0L) { return _localPlayerId; } PlayerAgent val = TryGetLocalPlayer(); if ((Object)val == (Object)null) { return 0uL; } RegisterPlayer(val); return _localPlayerId; } private bool ShouldCollectPeerOwnedMetric(PlayerAgent? player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown if ((Object)player == (Object)null) { return false; } try { SNet_Player owner = player.Owner; if ((Object)owner == (Object)null) { return true; } if (owner.IsLocal || owner.IsBot) { return true; } return false; } catch { return true; } } private bool ShouldCollectSharedMetric(PlayerAgent? player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown if ((Object)player == (Object)null) { return false; } try { SNet_Player owner = player.Owner; if ((Object)owner == (Object)null) { return true; } if (owner.IsLocal || owner.IsBot) { return true; } return IsHostAuthority && IsRemoteHumanPlayer(player); } catch { return true; } } private static bool ShouldCollectLocalMetric(PlayerAgent? player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown if ((Object)player == (Object)null) { return false; } try { SNet_Player owner = player.Owner; return (Object)owner == (Object)null || owner.IsLocal; } catch { return true; } } private static bool IsLocalHumanPlayer(PlayerAgent? player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown if ((Object)player == (Object)null) { return false; } try { SNet_Player owner = player.Owner; return (Object)owner != (Object)null && owner.IsLocal && !owner.IsBot; } catch { return false; } } private PlayerAgent? TryGetSingleKnownPlayer() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_003c: Expected O, but got Unknown PlayerAgent val = null; foreach (PlayerAgent value in _playersByUnityId.Values) { if ((Object)val != (Object)null && (Object)val != (Object)value) { return null; } val = value; } return val; } private static List GetCurrentBioscanPlayers(CP_Bioscan_Core core) { //IL_0008: 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) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) List list = new List(4); try { pBioscanState state = core.State; if ((int)state.status != 3 || state.playersInScan <= 0) { return list; } AddBioscanPlayer(list, state.playerInScan1); AddBioscanPlayer(list, state.playerInScan2); AddBioscanPlayer(list, state.playerInScan3); AddBioscanPlayer(list, state.playerInScan4); } catch { } return list; } private static void AddBioscanPlayer(List players, pPlayer packedPlayer) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown try { SNet_Player val = null; if (!((pPlayer)(ref packedPlayer)).TryGetPlayer(ref val) || (Object)val == (Object)null) { return; } int num = val.PlayerSlotIndex(); if (num >= 0) { PlayerAgent val2 = FindPlayerAgentInSlot(num); if ((Object)val2 != (Object)null && !players.Contains(val2)) { players.Add(val2); } } } catch { } } private PlayerAgent? ResolvePlayer(Agent? agent) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown if ((Object)agent == (Object)null) { return null; } PlayerAgent val = (PlayerAgent)((agent is PlayerAgent) ? agent : null); if ((Object)(object)val != (Object)null) { return val; } if (agent.GameObjectID > 0 && _playersByAgentId.TryGetValue(agent.GameObjectID, out PlayerAgent value)) { return value; } int instanceID = ((Object)agent).GetInstanceID(); if (instanceID != 0 && _playersByUnityId.TryGetValue(instanceID, out PlayerAgent value2)) { return value2; } return null; } private PlayerAgent? ResolvePlayerFromObject(object? source) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown if (source == null) { return null; } PlayerAgent val = (PlayerAgent)((source is PlayerAgent) ? source : null); if ((Object)(object)val != (Object)null) { return val; } Type type = source.GetType(); string[] array = new string[11] { "Owner", "owner", "m_owner", "OwnerAgent", "m_ownerAgent", "Player", "player", "SourcePlayer", "m_agent", "Agent", "agent" }; foreach (string name in array) { try { object obj = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(source); PlayerAgent val2 = (PlayerAgent)((obj is PlayerAgent) ? obj : null); if ((Object)(object)val2 != (Object)null) { return val2; } object obj2 = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(source); PlayerAgent val3 = (PlayerAgent)((obj2 is PlayerAgent) ? obj2 : null); if ((Object)(object)val3 != (Object)null) { return val3; } } catch { } } return null; } private static bool IsObjectivePickup(object? pickup) { if (pickup == null) { return false; } string text = pickup.GetType().FullName ?? pickup.GetType().Name; if (text.IndexOf("ArtifactPickup_Core", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("CarryItemPickup_Core", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } try { Type type = pickup.GetType(); string[] array = new string[4] { "m_isWardenObjective", "IsWardenObjective", "ObjectiveItemSolved", "WardenObjectiveChainIndex" }; foreach (string name in array) { PropertyInfo property = type.GetProperty(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { object value = property.GetValue(pickup); if (value is bool result) { return result; } if (value is int num) { return num >= 0; } } FieldInfo field = type.GetField(name, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object value2 = field.GetValue(pickup); if (value2 is bool result2) { return result2; } if (value2 is int num2) { return num2 >= 0; } } } } catch { } return false; } private PlayerAgent? ResolveReviveTarget(Interact_Revive interaction, PlayerAgent source) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown int instanceID = ((Object)interaction).GetInstanceID(); if (_reviveTargetsByInteraction.TryGetValue(instanceID, out PlayerAgent value) && (Object)value != (Object)null && GetPlayerId(value) != GetPlayerId(source)) { _reviveTargetsByInteraction.Remove(instanceID); return value; } try { PlayerAgent interactTargetAgent = ((Interact_Timed)interaction).m_interactTargetAgent; if ((Object)interactTargetAgent != (Object)null && GetPlayerId(interactTargetAgent) != GetPlayerId(source)) { return interactTargetAgent; } } catch { } return null; } private static WeaponScoreStats GetWeaponStats(PlayerScoreStats stats, DamageSourceType sourceType) { if (sourceType != DamageSourceType.SecondaryWeapon) { return stats.PrimaryWeapon; } return stats.SecondaryWeapon; } private void CountDamageTaken(PlayerAgent player, float amount) { amount = Mathf.Max(0f, amount); if (!(amount <= 0f)) { RegisterPlayer(player); ulong playerId = GetPlayerId(player); float time = Time.time; if (!_lastDamageTakenAt.TryGetValue(playerId, out var value) || !_lastDamageTakenAmount.TryGetValue(playerId, out var value2) || !(Mathf.Abs(value2 - amount) <= 0.01f) || !(time - value <= 0.05f)) { _lastDamageTakenAt[playerId] = time; _lastDamageTakenAmount[playerId] = amount; GetOrCreateStats(playerId).DamageTaken += amount; } } } private void CountKillFromAttribution(EnemyAgent enemy, Dam_EnemyDamageBase damageBase, EnemyAttribution attribution) { ulong lastDamageDealer = attribution.LastDamageDealer; if (lastDamageDealer != 0L) { PlayerScoreStats orCreateStats = GetOrCreateStats(lastDamageDealer); float num = Mathf.Max(attribution.LastKnownHealthMax, GetEnemyMaxHealth(enemy, damageBase)); if (attribution.IsScout || IsScout(enemy)) { orCreateStats.ScoutKills++; } else if (num >= 300f) { orCreateStats.BossKills++; } else if (num >= 100f && num < 300f) { orCreateStats.LargeKills++; } else { orCreateStats.SmallKills++; } if (attribution.LastDamageWasMelee || attribution.LastDamageSource == DamageSourceType.Melee) { orCreateStats.MeleeKills++; } if (attribution.LastDamageSource == DamageSourceType.Tool) { orCreateStats.ToolKills++; } if (attribution.LastDamageWasSleepingTarget) { orCreateStats.StealthKills++; } } } private void CountWeaponShots(ulong playerId, DamageSourceType sourceType, int count) { if (count > 0 && (sourceType == DamageSourceType.PrimaryWeapon || sourceType == DamageSourceType.SecondaryWeapon)) { GetWeaponStats(GetOrCreateStats(playerId), sourceType).ShotsFired += count; } } private void CountReceivedEnemyDamage(Dam_EnemyDamageBase damageBase, Agent? sourceAgent, float damage, int limbId, DamageSourceType fallbackSourceType, bool isMelee, bool wasSleepingTarget) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown if (damage <= 0f) { return; } EnemyAgent enemyFromDamageBase = GetEnemyFromDamageBase(damageBase); PlayerAgent val = ResolvePlayer(sourceAgent) ?? ResolvePlayerFromObject(sourceAgent); DamageSourceType damageSourceType = fallbackSourceType; if (!isMelee && (Object)val != (Object)null) { damageSourceType = ResolveWeaponSlot(val); } ulong num = (((Object)val != (Object)null) ? GetPlayerId(val) : 0); IntPtr enemyKey = GetEnemyKey(enemyFromDamageBase, damageBase); EnemyAttribution enemyAttribution = ((enemyKey != IntPtr.Zero) ? GetOrCreateAttribution(enemyKey) : null); bool flag = IsWeakspotLimb(damageBase, limbId); bool flag2 = num != 0L && ShouldCollectSharedMetric(val); if (flag2) { RegisterPlayer(val); CountEnemyDamage(enemyAttribution, num, damage, damageSourceType, flag, isMelee); } if (enemyAttribution != null) { if (flag2) { enemyAttribution.LastDamageDealer = num; enemyAttribution.LastDamageTime = Time.time; enemyAttribution.LastDamageSource = damageSourceType; enemyAttribution.LastDamageWasWeakspot = flag; enemyAttribution.LastDamageWasMelee = isMelee; enemyAttribution.LastDamageWasSleepingTarget = wasSleepingTarget || IsEnemySleeping(enemyFromDamageBase); } enemyAttribution.LastKnownHealthMax = Mathf.Max(enemyAttribution.LastKnownHealthMax, GetEnemyMaxHealth(enemyFromDamageBase, damageBase)); enemyAttribution.EnemyDataId = SafeEnemyDataId(enemyFromDamageBase); enemyAttribution.IsScout |= IsScout(enemyFromDamageBase); } } private void CountEnemyDamage(EnemyAttribution? attribution, ulong playerId, float damage, DamageSourceType sourceType, bool isWeakspot, bool isMelee) { float num = Mathf.Max(0f, damage); if (num <= 0f) { return; } float time = Time.time; if (attribution != null && attribution.LastCountedDamageDealer == playerId && Mathf.Abs(attribution.LastCountedDamageAmount - num) <= 0.01f && time - attribution.LastCountedDamageTime <= 0.05f) { return; } PlayerScoreStats orCreateStats = GetOrCreateStats(playerId); orCreateStats.TotalDamage += num; if (!isMelee && (sourceType == DamageSourceType.PrimaryWeapon || sourceType == DamageSourceType.SecondaryWeapon)) { WeaponScoreStats weaponStats = GetWeaponStats(orCreateStats, sourceType); weaponStats.Hits++; if (isWeakspot) { weaponStats.WeakspotHits++; } } if (attribution != null) { attribution.LastCountedDamageDealer = playerId; attribution.LastCountedDamageTime = time; attribution.LastCountedDamageAmount = num; } } private static DamageSourceType ResolveDamageSourceType(PlayerAgent? sourcePlayer, Agent? sourceAgent) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown if ((Object)sourcePlayer != (Object)null) { try { FirstPersonItemHolder fPItemHolder = sourcePlayer.FPItemHolder; object obj; if ((Object)(object)fPItemHolder == (Object)null) { obj = null; } else { ItemEquippable wieldedItem = fPItemHolder.WieldedItem; obj = (((Object)(object)wieldedItem != (Object)null) ? ((Il2CppObjectBase)wieldedItem).TryCast() : null); } BulletWeapon val = (BulletWeapon)obj; if ((Object)val != (Object)null) { return ResolveWeaponSlot(val); } } catch { } return DamageSourceType.PrimaryWeapon; } if (!IsToolSourceName(((Object)sourceAgent == (Object)null) ? string.Empty : (((object)sourceAgent).GetType().FullName ?? ((object)sourceAgent).GetType().Name))) { return DamageSourceType.Unknown; } return DamageSourceType.Tool; } private static DamageSourceType ResolveNativeDamageSourceType(PlayerAgent? sourcePlayer, Agent? sourceAgent, uint gearCategoryId) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown try { if (AchievementManager.IsMelee(gearCategoryId)) { return DamageSourceType.Melee; } } catch { } if (IsToolSourceName(((Object)sourceAgent == (Object)null) ? string.Empty : (((object)sourceAgent).GetType().FullName ?? ((object)sourceAgent).GetType().Name))) { return DamageSourceType.Tool; } if (!((Object)sourcePlayer != (Object)null)) { return DamageSourceType.Unknown; } return ResolveWeaponSlot(sourcePlayer); } private static ResourcePackKind? ResolveResourcePackKind(eResourceContainerSpawnType type) { //IL_0000: 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_000c: Invalid comparison between Unknown and I4 //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 if ((int)type == 0) { return ResourcePackKind.Medical; } if ((int)type == 1) { return ResourcePackKind.Ammo; } if ((int)type == 2) { return ResourcePackKind.Tool; } if ((int)type == 9) { return ResourcePackKind.Disinfection; } return null; } private static DamageSourceType ResolveWeaponSlot(BulletWeapon weapon) { //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) try { string text = ((object)((ItemEquippable)weapon).AmmoType/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(); if (text.Contains("special") || text.Contains("secondary")) { return DamageSourceType.SecondaryWeapon; } } catch { } return DamageSourceType.PrimaryWeapon; } private unsafe static DamageSourceType ResolveWeaponSlot(InventorySlot slot) { string text = ((object)(*(InventorySlot*)(&slot))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(); if (text.Contains("special") || text.Contains("secondary")) { return DamageSourceType.SecondaryWeapon; } if (text.Contains("standard") || text.Contains("primary")) { return DamageSourceType.PrimaryWeapon; } return DamageSourceType.Unknown; } private static DamageSourceType ResolveWeaponSlot(PlayerAgent player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_00d7: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown DamageSourceType damageSourceType = ResolveWeaponSlot(SafeWieldedSlot(player)); if (damageSourceType != DamageSourceType.Unknown) { return damageSourceType; } try { PlayerSync sync = player.Sync; object obj = ((!((Object)(object)sync == (Object)null)) ? ((object)sync.GetWieldedSlot()/*cast due to .constrained prefix*/).ToString().ToLowerInvariant() : null); if (obj == null) { obj = string.Empty; } string text = (string)obj; if (text.Contains("secondary") || text.Contains("special")) { return DamageSourceType.SecondaryWeapon; } if (text.Contains("primary") || text.Contains("standard")) { return DamageSourceType.PrimaryWeapon; } } catch { } try { FirstPersonItemHolder fPItemHolder = player.FPItemHolder; object obj3; if ((Object)(object)fPItemHolder == (Object)null) { obj3 = null; } else { ItemEquippable wieldedItem = fPItemHolder.WieldedItem; obj3 = (((Object)(object)wieldedItem != (Object)null) ? ((Il2CppObjectBase)wieldedItem).TryCast() : null); } BulletWeapon val = (BulletWeapon)obj3; if ((Object)val != (Object)null) { return ResolveWeaponSlot(val); } } catch { } return DamageSourceType.PrimaryWeapon; } private static Agent? ResolveAgent(pAgent source) { try { Agent val = null; return ((pAgent)(ref source)).TryGet(ref val) ? val : null; } catch { return null; } } private static int SafeFireCount(PlayerAgent player) { try { PlayerSync sync = player.Sync; return Mathf.Max(0, ((Object)(object)sync != (Object)null) ? sync.FireCountSync : 0); } catch { return 0; } } private static InventorySlot SafeWieldedSlot(PlayerAgent player) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0025: 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_001d: Unknown result type (might be due to invalid IL or missing references) try { return player.Inventory.WieldedSlot; } catch { } try { return player.Sync.GetWieldedSlot(); } catch { return (InventorySlot)0; } } private static int AdjustSyncedShotCountForWeapon(PlayerAgent player, int fireCount) { return Mathf.Max(1, fireCount) * ResolveProjectileCountPerShot(player); } private static int ResolveProjectileCountPerShot(PlayerAgent player) { int num = TryGetShotgunProjectileCount(SafeWieldedItem(player)); if (num > 0) { return num; } num = TryGetShotgunProjectileCount(SafeFirstPersonWieldedItem(player)); if (num > 0) { return num; } return 1; } private static ItemEquippable? SafeWieldedItem(PlayerAgent player) { try { PlayerInventoryBase inventory = player.Inventory; return ((Object)(object)inventory != (Object)null) ? inventory.WieldedItem : null; } catch { return null; } } private static ItemEquippable? SafeFirstPersonWieldedItem(PlayerAgent player) { try { FirstPersonItemHolder fPItemHolder = player.FPItemHolder; return ((Object)(object)fPItemHolder != (Object)null) ? fPItemHolder.WieldedItem : null; } catch { return null; } } private static int TryGetShotgunProjectileCount(ItemEquippable? item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown if ((Object)item == (Object)null) { return 0; } try { Shotgun val = ((Il2CppObjectBase)item).TryCast(); if ((Object)val != (Object)null && ((ItemEquippable)val).ArchetypeData != null) { return Mathf.Max(1, ((ItemEquippable)val).ArchetypeData.ShotgunBulletCount); } } catch { } try { ShotgunSynced val2 = ((Il2CppObjectBase)item).TryCast(); if ((Object)val2 != (Object)null && ((ItemEquippable)val2).ArchetypeData != null) { return Mathf.Max(1, ((ItemEquippable)val2).ArchetypeData.ShotgunBulletCount); } } catch { } return 0; } private static bool IsRemoteHumanPlayer(PlayerAgent player) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown try { return (Object)player.Owner != (Object)null && !player.Owner.IsLocal && !player.Owner.IsBot; } catch { return false; } } private static PlayerAgent? FindPlayerAgentInSlot(int slot) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown try { List playerAgentsInLevel = PlayerManager.PlayerAgentsInLevel; if (playerAgentsInLevel == null) { return null; } for (int i = 0; i < playerAgentsInLevel.Count; i++) { PlayerAgent val = playerAgentsInLevel[i]; if ((Object)val != (Object)null && SafePlayerSlot(val) == slot) { return val; } } } catch { } return null; } private static bool IsToolSourceName(string typeName) { if (typeName.IndexOf("sentry", StringComparison.OrdinalIgnoreCase) < 0 && typeName.IndexOf("mine", StringComparison.OrdinalIgnoreCase) < 0 && typeName.IndexOf("trip", StringComparison.OrdinalIgnoreCase) < 0) { return typeName.IndexOf("tool", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static IntPtr GetEnemyKey(EnemyAgent? enemy, Dam_EnemyDamageBase? damageBase) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown try { if ((Object)enemy != (Object)null) { return new IntPtr(((Object)enemy).GetInstanceID()); } if ((Object)damageBase != (Object)null) { return new IntPtr(((Object)damageBase).GetInstanceID()); } } catch { } return IntPtr.Zero; } private static EnemyAgent? GetEnemyFromDamageBase(Dam_EnemyDamageBase damageBase) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown try { if ((Object)damageBase.Owner != (Object)null) { return damageBase.Owner; } } catch { } try { Agent baseAgent = ((Dam_SyncedDamageBase)damageBase).GetBaseAgent(); return (EnemyAgent)((baseAgent is EnemyAgent) ? baseAgent : null); } catch { return null; } } private static bool IsWeakspotLimb(Dam_EnemyDamageBase damageBase, int limbId) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 try { if (limbId < 0 || damageBase.DamageLimbs == null || limbId >= ((Il2CppArrayBase)(object)damageBase.DamageLimbs).Count) { return false; } Dam_EnemyDamageLimb val = ((Il2CppArrayBase)(object)damageBase.DamageLimbs)[limbId]; return (Object)val != (Object)null && (int)val.m_type == 1; } catch { return false; } } private static bool IsEnemySleeping(EnemyAgent? enemy) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //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) if ((Object)enemy == (Object)null) { return false; } try { EnemyLocomotion locomotion = enemy.Locomotion; object obj = ((!((Object)(object)locomotion == (Object)null)) ? ((object)locomotion.CurrentStateEnum/*cast due to .constrained prefix*/).ToString() : null); if (obj == null) { obj = string.Empty; } string text = (string)obj; return text.IndexOf("hibernate", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("sleep", StringComparison.OrdinalIgnoreCase) >= 0; } catch { return false; } } private static bool IsScout(EnemyAgent? enemy) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if ((Object)enemy == (Object)null) { return false; } if (ContainsScoutText(SafeReflectEnemyDataValue(enemy, "enemyType")) || ContainsScoutText(SafeReflectEnemyDataValue(enemy, "EnemyType")) || ContainsScoutText(SafeReflectEnemyDataValue(enemy, "name")) || ContainsScoutText(SafeReflectEnemyDataValue(enemy, "publicName"))) { return true; } try { return ((Object)enemy).name.IndexOf("scout", StringComparison.OrdinalIgnoreCase) >= 0; } catch { return false; } } private static string SafeReflectEnemyDataValue(EnemyAgent enemy, string memberName) { try { EnemyDataBlock enemyData = enemy.EnemyData; if (enemyData == null) { return string.Empty; } Type type = ((object)enemyData).GetType(); PropertyInfo property = type.GetProperty(memberName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return Convert.ToString(property.GetValue(enemyData), CultureInfo.InvariantCulture) ?? string.Empty; } FieldInfo field = type.GetField(memberName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return Convert.ToString(field.GetValue(enemyData), CultureInfo.InvariantCulture) ?? string.Empty; } } catch { } return string.Empty; } private static bool ContainsScoutText(string value) { return value.IndexOf("scout", StringComparison.OrdinalIgnoreCase) >= 0; } private static float GetEnemyMaxHealth(EnemyAgent? enemy, Dam_EnemyDamageBase? damageBase) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown float num = 0f; try { if ((Object)damageBase != (Object)null) { num = Mathf.Max(num, ((Dam_SyncedDamageBase)damageBase).HealthMax); } } catch { } try { if ((Object)(((Object)(object)enemy != (Object)null) ? enemy.Damage : null) != (Object)null) { num = Mathf.Max(num, ((Dam_SyncedDamageBase)enemy.Damage).HealthMax); } } catch { } try { float num2 = num; float? num3; if ((Object)(object)enemy == (Object)null) { num3 = null; } else { EnemyBalancingDataBlock enemyBalancingData = enemy.EnemyBalancingData; if (enemyBalancingData == null) { num3 = null; } else { HealthData health = enemyBalancingData.Health; num3 = ((health != null) ? new float?(health.HealthMax) : ((float?)null)); } } float? num4 = num3; num = Mathf.Max(num2, num4.GetValueOrDefault()); } catch { } return num; } private static uint SafeEnemyDataId(EnemyAgent? enemy) { try { return ((Object)(object)enemy != (Object)null) ? enemy.EnemyDataID : 0u; } catch { return 0u; } } private static bool IsPlayerDown(PlayerAgent player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown try { if (!((Agent)player).Alive) { return true; } return (Object)player.Damage != (Object)null && ((Dam_SyncedDamageBase)player.Damage).Health <= 0f; } catch { return false; } } public static ulong GetPlayerId(PlayerAgent player) { if (player.PlayerSlotIndex >= 0) { return (ulong)(player.PlayerSlotIndex + 1); } if (((Agent)player).GameObjectID > 0) { return 1000000uL + (ulong)((Agent)player).GameObjectID; } return 2000000uL + (ulong)Math.Abs(((Object)player).GetInstanceID()); } private static int SafePlayerSlot(PlayerAgent player) { try { return player.PlayerSlotIndex; } catch { return -1; } } private static string SafePlayerName(PlayerAgent player, string fallback) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown try { if ((Object)player.Owner != (Object)null) { string nickName = player.Owner.NickName; if (!string.IsNullOrWhiteSpace(nickName)) { return nickName; } } } catch { } try { string name = ((Object)player).name; if (!string.IsNullOrWhiteSpace(name)) { return name; } } catch { } if (!string.IsNullOrWhiteSpace(fallback)) { return fallback; } return "Player"; } private static ulong SafeSteamId(PlayerAgent player) { try { SNet_Player owner = player.Owner; return ((Object)(object)owner != (Object)null) ? owner.Lookup : 0; } catch { return 0uL; } } private static bool HasAnyStat(PlayerScoreStats player) { if (player.SmallKills + player.LargeKills + player.ScoutKills + player.BossKills + player.PrimaryWeapon.ShotsFired + player.SecondaryWeapon.ShotsFired + player.PrimaryWeapon.Hits + player.SecondaryWeapon.Hits + player.DownedCount + player.RescueCount + player.MedicalPacksReceived + player.AmmoPacksReceived + player.ToolPacksReceived + player.DisinfectionPacksReceived + player.ObjectivePickups + player.TerminalCommands + player.MeleeKills + player.ToolKills + player.BioTrackerTaggedEnemies + player.StealthKills <= 0 && !(player.TotalDamage > 0f) && !(player.DamageTaken > 0f)) { return player.ScanParticipationSeconds > 0f; } return true; } private static bool ShouldExportPlayer(PlayerScoreStats player) { if (!HasAnyStat(player) && player.PlayerSlot < 0) { return player.SteamId != 0; } return true; } private void Verbose(string message) { if (_verboseEventLog()) { ScoreboardLogger.Info("[Event] " + message); } } } public sealed class ScoreboardSnapshot { public static readonly ScoreboardSnapshot Empty = new ScoreboardSnapshot(); public int ProtocolVersion { get; set; } = 2; public string PluginVersion { get; set; } = string.Empty; public bool IsHostAuthoritative { get; set; } public bool ExpeditionEnded { get; set; } public bool HasObjectivePickups { get; set; } public long Timestamp { get; set; } public List Players { get; set; } = new List(); public ScoreboardSnapshot Clone() { return new ScoreboardSnapshot { ProtocolVersion = ProtocolVersion, PluginVersion = PluginVersion, IsHostAuthoritative = IsHostAuthoritative, ExpeditionEnded = ExpeditionEnded, HasObjectivePickups = HasObjectivePickups, Timestamp = Timestamp, Players = Players.Select((PlayerScoreStats player) => player.Clone()).ToList() }; } public void MergeMax(ScoreboardSnapshot other) { ExpeditionEnded |= other.ExpeditionEnded; HasObjectivePickups |= other.HasObjectivePickups; Timestamp = Math.Max(Timestamp, other.Timestamp); foreach (PlayerScoreStats incoming in other.Players) { PlayerScoreStats playerScoreStats = Players.FirstOrDefault((PlayerScoreStats player) => player.PlayerId == incoming.PlayerId); if (playerScoreStats == null) { Players.Add(incoming.Clone()); } else { playerScoreStats.MergeMax(incoming); } } Players = (from player in Players orderby (player.PlayerSlot < 0) ? int.MaxValue : player.PlayerSlot, player.PlayerId select player).ToList(); } } public sealed class WeaponScoreStats { public int ShotsFired { get; set; } public int Hits { get; set; } public int WeakspotHits { get; set; } public float HitRate { get { if (ShotsFired > 0) { return (float)Hits / (float)ShotsFired; } return 0f; } } public float WeakspotRate { get { if (Hits > 0) { return (float)WeakspotHits / (float)Hits; } return 0f; } } public WeaponScoreStats Clone() { return new WeaponScoreStats { ShotsFired = ShotsFired, Hits = Hits, WeakspotHits = WeakspotHits }; } public void MergeMax(WeaponScoreStats other) { ShotsFired = Math.Max(ShotsFired, other.ShotsFired); Hits = Math.Max(Hits, other.Hits); WeakspotHits = Math.Max(WeakspotHits, other.WeakspotHits); } } } namespace KainAdvancedScoreboard.Patches { [HarmonyPatch] public static class ScoreboardGameplayPatches { [HarmonyPostfix] [HarmonyPatch(typeof(BulletWeapon), "Fire")] public static void BulletWeapon_Fire_Postfix(BulletWeapon __instance, bool __runOriginal) { if (__runOriginal && !(__instance is Shotgun)) { BulletWeapon __instance2 = __instance; Safe(delegate { Plugin.OnBulletWeaponFired(__instance2); }); } } [HarmonyPostfix] [HarmonyPatch(typeof(Shotgun), "Fire")] public static void Shotgun_Fire_Postfix(Shotgun __instance, bool __runOriginal) { if (__runOriginal) { Shotgun __instance2 = __instance; Safe(delegate { Plugin.OnBulletWeaponFired((BulletWeapon?)(object)__instance2); }); } } [HarmonyPostfix] [HarmonyPatch(typeof(BulletWeaponSynced), "Fire")] public static void BulletWeaponSynced_Fire_Postfix(BulletWeaponSynced __instance, bool __runOriginal) { if (__runOriginal && !(__instance is ShotgunSynced) && IsBotOwner((__instance != null) ? ((Item)__instance).Owner : null)) { PlayerSync sync = ((Item)__instance).Owner.Sync; Safe(delegate { Plugin.OnSyncedBulletsFired(sync, 1); }); } } [HarmonyPostfix] [HarmonyPatch(typeof(ShotgunSynced), "Fire")] public static void ShotgunSynced_Fire_Postfix(ShotgunSynced __instance, bool __runOriginal) { if (__runOriginal && IsBotOwner((__instance != null) ? ((Item)__instance).Owner : null)) { PlayerSync sync = ((Item)__instance).Owner.Sync; Safe(delegate { Plugin.OnSyncedBulletsFired(sync, 1); }); } } [HarmonyPrefix] [HarmonyPatch(typeof(Dam_EnemyDamageBase), "BulletDamage")] public static void Enemy_BulletDamage_Prefix(Dam_EnemyDamageBase __instance, Agent sourceAgent, ref float dam, int limbID) { float damage = dam; Safe(delegate { Plugin.OnEnemyDamaged(__instance, sourceAgent, damage, limbID, isMelee: false, wasSleepingTarget: false); }); } [HarmonyPrefix] [HarmonyPatch(typeof(Dam_EnemyDamageBase), "MeleeDamage")] public static void Enemy_MeleeDamage_Prefix(Dam_EnemyDamageBase __instance, Agent sourceAgent, ref float dam, int limbID, float sleeperMulti) { float damage = dam; bool wasSleepingTarget = sleeperMulti > 1.01f; Safe(delegate { Plugin.OnEnemyDamaged(__instance, sourceAgent, damage, limbID, isMelee: true, wasSleepingTarget); }); } [HarmonyPostfix] [HarmonyPatch(typeof(EnemyAgent), "OnDead")] public static void EnemyAgent_OnDead_Postfix(EnemyAgent __instance) { Safe(delegate { Plugin.OnEnemyDead(__instance); }); } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerAgent), "GiveHealth")] public static void PlayerAgent_GiveHealth_Postfix(PlayerAgent __instance, PlayerAgent giverAgent, float amountRel) { Safe(delegate { Plugin.OnGiveHealth(__instance, giverAgent, amountRel); }); } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerAgent), "GiveAmmoRel")] public static void PlayerAgent_GiveAmmoRel_Postfix(PlayerAgent __instance, PlayerAgent giverAgent, float ammoStandardRel, float ammoSpecialRel, float ammoClassRel) { Safe(delegate { Plugin.OnGiveAmmo(__instance, giverAgent, ammoStandardRel, ammoSpecialRel, ammoClassRel); }); } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerAgent), "GiveDisinfection")] public static void PlayerAgent_GiveDisinfection_Postfix(PlayerAgent __instance, PlayerAgent giverAgent, float amountRel) { Safe(delegate { Plugin.OnGiveDisinfection(__instance, giverAgent, amountRel); }); } [HarmonyPrefix] [HarmonyPatch(typeof(Dam_PlayerDamageBase), "OnIncomingDamage")] public static void PlayerDamageBase_OnIncomingDamage_Prefix(Dam_PlayerDamageBase __instance, ref float damage, ref float originalDamage, Agent sourceAgent) { float currentDamage = damage; float currentOriginalDamage = originalDamage; Safe(delegate { Dam_PlayerDamageBase val = __instance; Plugin.OnIncomingPlayerDamage(((Object)(object)val != (Object)null) ? val.Owner : null, currentDamage, currentOriginalDamage, sourceAgent); }); } [HarmonyPostfix] [HarmonyPatch(typeof(Interact_Revive), "Setup")] public static void Revive_Setup_Postfix(Interact_Revive __instance, PlayerAgent owner) { Safe(delegate { Plugin.OnReviveSetup(__instance, owner); }); } [HarmonyPostfix] [HarmonyPatch(typeof(Interact_Revive), "TriggerInteractionAction")] public static void Revive_TriggerInteractionAction_Postfix(Interact_Revive __instance, PlayerAgent source) { Safe(delegate { Plugin.OnReviveCompleted(__instance, source); }); } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerInventorySynced), "GetSync")] public static void PlayerInventorySynced_GetSync_Prefix(PlayerInventorySynced __instance) { Safe(delegate { Plugin.OnPlayerInventorySyncedGetSync(__instance); }); } [HarmonyPostfix] [HarmonyPatch(typeof(CP_Bioscan_Core), "Update")] public static void Bioscan_Update_Postfix(CP_Bioscan_Core __instance) { Safe(delegate { Plugin.OnBioscanUpdated(__instance); }); } private static bool IsBotOwner(PlayerAgent? player) { try { return (Object)(object)((player != null) ? player.Owner : null) != (Object)null && player.Owner.IsBot; } catch { return false; } } private static void Safe(Action action) { try { action(); } catch (Exception ex) { ScoreboardLogger.Error("KAS gameplay patch failed.", ex); } } } [HarmonyPatch] public static class ScoreboardUtilityPatches { [HarmonyPostfix] [HarmonyPatch(typeof(ToolSyncManager), "WantToTagEnemy")] public static void ToolSyncManager_WantToTagEnemy_Postfix(EnemyAgent enemy) { Safe(delegate { Plugin.OnEnemyTagged(enemy); }); } [HarmonyPostfix] [HarmonyPatch(typeof(LG_ComputerTerminalCommandInterpreter), "ReceiveCommand")] public static void Terminal_ReceiveCommand_Postfix(LG_ComputerTerminalCommandInterpreter __instance) { Safe(delegate { Plugin.OnTerminalCommand(__instance); }); } [HarmonyPostfix] [HarmonyPatch(typeof(ArtifactPickup_Core), "OnInteractionPickUp")] public static void ArtifactPickup_OnInteractionPickUp_Postfix(ArtifactPickup_Core __instance, PlayerAgent player) { Safe(delegate { Plugin.OnObjectivePickup(__instance, player); }); } [HarmonyPostfix] [HarmonyPatch(typeof(CarryItemPickup_Core), "OnInteractionPickUp")] public static void CarryItemPickup_OnInteractionPickUp_Postfix(CarryItemPickup_Core __instance, PlayerAgent player) { Safe(delegate { Plugin.OnObjectivePickup(__instance, player); }); } private static void Safe(Action action) { try { action(); } catch (Exception ex) { ScoreboardLogger.Error("KAS utility patch failed.", ex); } } } [HarmonyPatch] public static class ScoreboardNativeCallbackSetupPatch { [HarmonyPostfix] [HarmonyPatch(typeof(GlobalCallbacks), "Setup")] public static void GlobalCallbacks_Setup_Postfix() { Plugin.InstallNativeCallbacks(); } } [HarmonyPatch] public static class ScoreboardPlayerRegistrationPatch { private static readonly Dictionary NextRegisterAt = new Dictionary(); [HarmonyPostfix] [HarmonyPatch(typeof(PlayerAgent), "Update")] public static void PlayerAgent_Update_Postfix(PlayerAgent __instance) { Safe(delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown if (!((Object)__instance == (Object)null)) { int instanceID = ((Object)__instance).GetInstanceID(); float time = Time.time; if (!NextRegisterAt.TryGetValue(instanceID, out var value) || !(time < value)) { NextRegisterAt[instanceID] = time + 0.5f; Plugin.RegisterPlayer(__instance); } } }); } private static void Safe(Action action) { try { action(); } catch (Exception ex) { ScoreboardLogger.Error("KAS player registration patch failed.", ex); } } } } namespace KainAdvancedScoreboard.Network { public sealed class ScoreboardNetwork { private const string PeerSummaryEventName = "KAS.ScoreboardPeerSummary.V1"; private const string HostStateEventName = "KAS.ScoreboardHostState.V1"; private readonly ScoreboardRuntime _runtime; private bool _registered; private bool _registrationFailedLogged; public ScoreboardNetwork(ScoreboardRuntime runtime) { _runtime = runtime; } public bool TryRegister() { if (_registered) { return true; } try { if (!NetworkAPI.IsEventRegistered("KAS.ScoreboardPeerSummary.V1")) { NetworkAPI.RegisterFreeSizedEvent("KAS.ScoreboardPeerSummary.V1", (Action)OnPeerSummaryBytesReceived); } if (!NetworkAPI.IsEventRegistered("KAS.ScoreboardHostState.V1")) { NetworkAPI.RegisterFreeSizedEvent("KAS.ScoreboardHostState.V1", (Action)OnHostStateBytesReceived); } _registered = true; ScoreboardLogger.Info("KAS network event registered."); return true; } catch (Exception ex) { if (!_registrationFailedLogged) { _registrationFailedLogged = true; ScoreboardLogger.Warning("KAS network API is not ready; scoreboard will run locally until registration succeeds. " + ex.Message); } return false; } } public void SyncSummary(bool force) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown try { if (TryRegister()) { if (_runtime.IsHostAuthority) { BroadcastHostSnapshot(); } else if (SNet.HasMaster && (Object)SNet.Master != (Object)null) { ScoreboardSnapshot snapshot = _runtime.CreatePeerSnapshot(); byte[] bytes = Encoding.UTF8.GetBytes(ScoreboardSnapshotCodec.Serialize(snapshot)); NetworkAPI.InvokeFreeSizedEvent("KAS.ScoreboardPeerSummary.V1", bytes, SNet.Master, (SNet_ChannelType)4); } } } catch (Exception ex) { ScoreboardLogger.Error("Failed to sync KAS scoreboard summary.", ex); } } private void OnPeerSummaryBytesReceived(ulong sender, byte[] bytes) { try { ScoreboardSnapshot scoreboardSnapshot = ScoreboardSnapshotCodec.Deserialize(Encoding.UTF8.GetString(bytes ?? Array.Empty())); if (scoreboardSnapshot != null && _runtime.IsHostAuthority && !scoreboardSnapshot.IsHostAuthoritative) { _runtime.MergePeerSnapshot(scoreboardSnapshot); BroadcastHostSnapshot(); } } catch (Exception ex) { ScoreboardLogger.Error("Failed to receive KAS peer scoreboard summary.", ex); } } private void OnHostStateBytesReceived(ulong sender, byte[] bytes) { try { ScoreboardSnapshot scoreboardSnapshot = ScoreboardSnapshotCodec.Deserialize(Encoding.UTF8.GetString(bytes ?? Array.Empty())); if (scoreboardSnapshot != null && scoreboardSnapshot.IsHostAuthoritative) { if (_runtime.IsHostAuthority) { _runtime.ApplyHostSnapshot(scoreboardSnapshot); } else if (IsFromCurrentHost(sender)) { _runtime.ApplyHostSnapshot(scoreboardSnapshot); } } } catch (Exception ex) { ScoreboardLogger.Error("Failed to receive KAS host scoreboard state.", ex); } } private void BroadcastHostSnapshot() { ScoreboardSnapshot scoreboardSnapshot = _runtime.CreateLocalSnapshot(); scoreboardSnapshot.IsHostAuthoritative = true; byte[] bytes = Encoding.UTF8.GetBytes(ScoreboardSnapshotCodec.Serialize(scoreboardSnapshot)); _runtime.ApplyHostSnapshot(scoreboardSnapshot); NetworkAPI.InvokeFreeSizedEvent("KAS.ScoreboardHostState.V1", bytes, (SNet_ChannelType)4); } private static bool IsFromCurrentHost(ulong sender) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown try { if (!SNet.IsOnline || !SNet.HasMaster || (Object)SNet.Master == (Object)null) { return true; } ulong lookup = SNet.Master.Lookup; return sender == 0L || lookup == 0L || sender == lookup; } catch { return true; } } } public static class ScoreboardSnapshotCodec { public static string Serialize(ScoreboardSnapshot snapshot) { StringBuilder stringBuilder = new StringBuilder(4096); stringBuilder.Append('{'); AppendProperty(stringBuilder, "ProtocolVersion", snapshot.ProtocolVersion); stringBuilder.Append(','); AppendProperty(stringBuilder, "PluginVersion", snapshot.PluginVersion); stringBuilder.Append(','); AppendProperty(stringBuilder, "IsHostAuthoritative", snapshot.IsHostAuthoritative); stringBuilder.Append(','); AppendProperty(stringBuilder, "ExpeditionEnded", snapshot.ExpeditionEnded); stringBuilder.Append(','); AppendProperty(stringBuilder, "HasObjectivePickups", snapshot.HasObjectivePickups); stringBuilder.Append(','); AppendProperty(stringBuilder, "Timestamp", snapshot.Timestamp); stringBuilder.Append(','); AppendString(stringBuilder, "Players"); stringBuilder.Append(':'); AppendPlayers(stringBuilder, snapshot.Players); stringBuilder.Append('}'); return stringBuilder.ToString(); } public static ScoreboardSnapshot? Deserialize(string json) { if (string.IsNullOrWhiteSpace(json)) { return null; } if (!(SimpleJson.Parse(json) is Dictionary dictionary)) { return null; } ScoreboardSnapshot scoreboardSnapshot = new ScoreboardSnapshot { ProtocolVersion = GetInt(dictionary, "ProtocolVersion", 2), PluginVersion = GetString(dictionary, "PluginVersion"), IsHostAuthoritative = GetBool(dictionary, "IsHostAuthoritative"), ExpeditionEnded = GetBool(dictionary, "ExpeditionEnded"), HasObjectivePickups = GetBool(dictionary, "HasObjectivePickups"), Timestamp = GetLong(dictionary, "Timestamp") }; if (dictionary.TryGetValue("Players", out var value) && value is List list) { foreach (object item in list) { if (item is Dictionary root) { scoreboardSnapshot.Players.Add(ReadPlayer(root)); } } } return scoreboardSnapshot; } private static void AppendPlayers(StringBuilder json, IEnumerable players) { json.Append('['); bool flag = true; foreach (PlayerScoreStats player in players) { if (!flag) { json.Append(','); } flag = false; AppendPlayer(json, player); } json.Append(']'); } private static void AppendPlayer(StringBuilder json, PlayerScoreStats player) { json.Append('{'); AppendProperty(json, "PlayerId", player.PlayerId); json.Append(','); AppendProperty(json, "PlayerName", player.PlayerName); json.Append(','); AppendProperty(json, "PlayerSlot", player.PlayerSlot); json.Append(','); AppendProperty(json, "SteamId", player.SteamId); json.Append(','); AppendProperty(json, "SmallKills", player.SmallKills); json.Append(','); AppendProperty(json, "LargeKills", player.LargeKills); json.Append(','); AppendProperty(json, "ScoutKills", player.ScoutKills); json.Append(','); AppendProperty(json, "BossKills", player.BossKills); json.Append(','); AppendProperty(json, "TotalDamage", player.TotalDamage); json.Append(','); AppendString(json, "PrimaryWeapon"); json.Append(':'); AppendWeapon(json, player.PrimaryWeapon); json.Append(','); AppendString(json, "SecondaryWeapon"); json.Append(':'); AppendWeapon(json, player.SecondaryWeapon); json.Append(','); AppendProperty(json, "DamageTaken", player.DamageTaken); json.Append(','); AppendProperty(json, "DownedCount", player.DownedCount); json.Append(','); AppendProperty(json, "RescueCount", player.RescueCount); json.Append(','); AppendProperty(json, "MedicalPacksReceived", player.MedicalPacksReceived); json.Append(','); AppendProperty(json, "AmmoPacksReceived", player.AmmoPacksReceived); json.Append(','); AppendProperty(json, "ToolPacksReceived", player.ToolPacksReceived); json.Append(','); AppendProperty(json, "DisinfectionPacksReceived", player.DisinfectionPacksReceived); json.Append(','); AppendProperty(json, "ObjectivePickups", player.ObjectivePickups); json.Append(','); AppendProperty(json, "TerminalCommands", player.TerminalCommands); json.Append(','); AppendProperty(json, "MeleeKills", player.MeleeKills); json.Append(','); AppendProperty(json, "ToolKills", player.ToolKills); json.Append(','); AppendProperty(json, "BioTrackerTaggedEnemies", player.BioTrackerTaggedEnemies); json.Append(','); AppendProperty(json, "ScanParticipationSeconds", player.ScanParticipationSeconds); json.Append(','); AppendProperty(json, "StealthKills", player.StealthKills); json.Append('}'); } private static void AppendWeapon(StringBuilder json, WeaponScoreStats weapon) { json.Append('{'); AppendProperty(json, "ShotsFired", weapon.ShotsFired); json.Append(','); AppendProperty(json, "Hits", weapon.Hits); json.Append(','); AppendProperty(json, "WeakspotHits", weapon.WeakspotHits); json.Append('}'); } private static PlayerScoreStats ReadPlayer(Dictionary root) { return new PlayerScoreStats { PlayerId = GetUlong(root, "PlayerId"), PlayerName = GetString(root, "PlayerName"), PlayerSlot = GetInt(root, "PlayerSlot", -1), SteamId = GetUlong(root, "SteamId"), SmallKills = GetInt(root, "SmallKills"), LargeKills = GetInt(root, "LargeKills"), ScoutKills = GetInt(root, "ScoutKills"), BossKills = GetInt(root, "BossKills"), TotalDamage = GetFloat(root, "TotalDamage"), PrimaryWeapon = ReadWeapon(GetObject(root, "PrimaryWeapon")), SecondaryWeapon = ReadWeapon(GetObject(root, "SecondaryWeapon")), DamageTaken = GetFloat(root, "DamageTaken"), DownedCount = GetInt(root, "DownedCount"), RescueCount = GetInt(root, "RescueCount"), MedicalPacksReceived = GetInt(root, "MedicalPacksReceived"), AmmoPacksReceived = GetInt(root, "AmmoPacksReceived"), ToolPacksReceived = GetInt(root, "ToolPacksReceived"), DisinfectionPacksReceived = GetInt(root, "DisinfectionPacksReceived"), ObjectivePickups = GetInt(root, "ObjectivePickups"), TerminalCommands = GetInt(root, "TerminalCommands"), MeleeKills = GetInt(root, "MeleeKills"), ToolKills = GetInt(root, "ToolKills"), BioTrackerTaggedEnemies = GetInt(root, "BioTrackerTaggedEnemies"), ScanParticipationSeconds = GetFloat(root, "ScanParticipationSeconds"), StealthKills = GetInt(root, "StealthKills") }; } private static WeaponScoreStats ReadWeapon(Dictionary? root) { if (root == null) { return new WeaponScoreStats(); } return new WeaponScoreStats { ShotsFired = GetInt(root, "ShotsFired"), Hits = GetInt(root, "Hits"), WeakspotHits = GetInt(root, "WeakspotHits") }; } private static void AppendProperty(StringBuilder json, string name, string value) { AppendString(json, name); json.Append(':'); AppendString(json, value); } private static void AppendProperty(StringBuilder json, string name, bool value) { AppendString(json, name); json.Append(':'); json.Append(value ? "true" : "false"); } private static void AppendProperty(StringBuilder json, string name, int value) { AppendString(json, name); json.Append(':'); json.Append(value.ToString(CultureInfo.InvariantCulture)); } private static void AppendProperty(StringBuilder json, string name, long value) { AppendString(json, name); json.Append(':'); json.Append(value.ToString(CultureInfo.InvariantCulture)); } private static void AppendProperty(StringBuilder json, string name, ulong value) { AppendString(json, name); json.Append(':'); json.Append(value.ToString(CultureInfo.InvariantCulture)); } private static void AppendProperty(StringBuilder json, string name, float value) { AppendString(json, name); json.Append(':'); json.Append(value.ToString("R", CultureInfo.InvariantCulture)); } private static void AppendString(StringBuilder json, string value) { json.Append('"'); string text = value ?? string.Empty; foreach (char c in text) { switch (c) { case '"': json.Append("\\\""); continue; case '\\': json.Append("\\\\"); continue; case '\b': json.Append("\\b"); continue; case '\f': json.Append("\\f"); continue; case '\n': json.Append("\\n"); continue; case '\r': json.Append("\\r"); continue; case '\t': json.Append("\\t"); continue; } if (char.IsControl(c)) { json.Append("\\u"); int num = c; json.Append(num.ToString("x4", CultureInfo.InvariantCulture)); } else { json.Append(c); } } json.Append('"'); } private static Dictionary? GetObject(Dictionary root, string key) { if (!root.TryGetValue(key, out object value)) { return null; } return value as Dictionary; } private static string GetString(Dictionary root, string key) { if (!root.TryGetValue(key, out object value)) { return string.Empty; } return Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty; } private static bool GetBool(Dictionary root, string key) { if (!root.TryGetValue(key, out object value)) { return false; } if (value is bool) { return (bool)value; } if (!(value is long num)) { if (value is double value2) { return Math.Abs(value2) > double.Epsilon; } bool result; return bool.TryParse(Convert.ToString(value, CultureInfo.InvariantCulture), out result) && result; } return num != 0; } private static int GetInt(Dictionary root, string key, int fallback = 0) { if (!root.TryGetValue(key, out object value)) { return fallback; } if (!(value is long num)) { if (!(value is double num2)) { if (value is int) { return (int)value; } if (!int.TryParse(Convert.ToString(value, CultureInfo.InvariantCulture), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return fallback; } return result; } return (int)num2; } return (int)num; } private static long GetLong(Dictionary root, string key) { if (!root.TryGetValue(key, out object value)) { return 0L; } if (value is long) { return (long)value; } if (!(value is double num)) { if (value is int num2) { return num2; } if (!long.TryParse(Convert.ToString(value, CultureInfo.InvariantCulture), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return 0L; } return result; } return (long)num; } private static ulong GetUlong(Dictionary root, string key) { if (!root.TryGetValue(key, out object value)) { return 0uL; } if (value is ulong) { return (ulong)value; } if (!(value is long num)) { if (value is double num2) { if (!(num2 >= 0.0)) { return 0uL; } return (ulong)num2; } if (!ulong.TryParse(Convert.ToString(value, CultureInfo.InvariantCulture), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return 0uL; } return result; } if (num < 0) { return 0uL; } return (ulong)num; } private static float GetFloat(Dictionary root, string key) { if (!root.TryGetValue(key, out object value)) { return 0f; } if (value is float) { return (float)value; } if (!(value is double num)) { if (!(value is long num2)) { if (value is int num3) { return num3; } if (!float.TryParse(Convert.ToString(value, CultureInfo.InvariantCulture), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return 0f; } return result; } return num2; } return (float)num; } } internal sealed class SimpleJson { private readonly string _json; private int _index; private SimpleJson(string json) { _json = json; } public static object? Parse(string json) { SimpleJson simpleJson = new SimpleJson(json); object result = simpleJson.ParseValue(); simpleJson.SkipWhitespace(); return result; } private object? ParseValue() { SkipWhitespace(); if (_index >= _json.Length) { throw new FormatException("Unexpected end of JSON."); } return _json[_index] switch { '{' => ParseObject(), '[' => ParseArray(), '"' => ParseString(), 't' => ParseLiteral("true", true), 'f' => ParseLiteral("false", false), 'n' => ParseLiteral("null", null), _ => ParseNumber(), }; } private Dictionary ParseObject() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); Expect('{'); SkipWhitespace(); if (TryConsume('}')) { return dictionary; } while (true) { string key = ParseString(); SkipWhitespace(); Expect(':'); dictionary[key] = ParseValue(); SkipWhitespace(); if (TryConsume('}')) { break; } Expect(','); } return dictionary; } private List ParseArray() { List list = new List(); Expect('['); SkipWhitespace(); if (TryConsume(']')) { return list; } while (true) { list.Add(ParseValue()); SkipWhitespace(); if (TryConsume(']')) { break; } Expect(','); } return list; } private string ParseString() { Expect('"'); List list = new List(); while (_index < _json.Length) { char c = _json[_index++]; switch (c) { case '"': return new string(list.ToArray()); default: list.Add(c); break; case '\\': { if (_index >= _json.Length) { throw new FormatException("Unexpected end of JSON escape sequence."); } char c2 = _json[_index++]; List list2 = list; list2.Add(c2 switch { '"' => '"', '\\' => '\\', '/' => '/', 'b' => '\b', 'f' => '\f', 'n' => '\n', 'r' => '\r', 't' => '\t', 'u' => ParseUnicodeEscape(), _ => throw new FormatException($"Unsupported JSON escape: \\{c2}"), }); break; } } } throw new FormatException("Unterminated JSON string."); } private char ParseUnicodeEscape() { if (_index + 4 > _json.Length) { throw new FormatException("Incomplete unicode escape."); } string s = _json.Substring(_index, 4); _index += 4; return (char)int.Parse(s, NumberStyles.HexNumber, CultureInfo.InvariantCulture); } private object ParseNumber() { int index = _index; while (_index < _json.Length && "-+0123456789.eE".IndexOf(_json[_index]) >= 0) { _index++; } string text = _json.Substring(index, _index - index); if (text.IndexOf('.') < 0 && text.IndexOf('e') < 0 && text.IndexOf('E') < 0 && long.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return result; } if (double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return result2; } throw new FormatException("Invalid JSON number: " + text); } private object? ParseLiteral(string literal, object? value) { if (_index + literal.Length > _json.Length || string.CompareOrdinal(_json, _index, literal, 0, literal.Length) != 0) { throw new FormatException($"Invalid JSON literal near index {_index}."); } _index += literal.Length; return value; } private bool TryConsume(char expected) { SkipWhitespace(); if (_index >= _json.Length || _json[_index] != expected) { return false; } _index++; return true; } private void Expect(char expected) { SkipWhitespace(); if (_index >= _json.Length || _json[_index] != expected) { throw new FormatException($"Expected '{expected}' near index {_index}."); } _index++; } private void SkipWhitespace() { while (_index < _json.Length && char.IsWhiteSpace(_json[_index])) { _index++; } } } } namespace KainAdvancedScoreboard.Localization { public static class ScoreboardLocalization { private static ScoreboardTextLanguage s_cachedLanguage = ScoreboardTextLanguage.English; private static string s_cachedGameLanguageName = string.Empty; private static DateTime s_nextRefreshAtUtc = DateTime.MinValue; public static bool UseChinese => CurrentLanguage == ScoreboardTextLanguage.Chinese; public static ScoreboardTextLanguage CurrentLanguage { get { RefreshIfNeeded(); return s_cachedLanguage; } } public static string CurrentGameLanguageName { get { RefreshIfNeeded(); return s_cachedGameLanguageName; } } public static string Title => Upper(Pick("Scoreboard", "计分板")); public static string HostSummary => Pick("Host authoritative summary", "Host 权威统计"); public static string LocalSummary => Pick("Local summary / waiting for host sync", "本地统计 / 等待 Host 同步"); public static string NoStats => Pick("No stats observed yet", "暂无统计数据"); public static string CloseHint => Pick("~/ESC close", "~/ESC 关闭"); public static string Pick(string english, string chinese) { string text = (UseChinese ? chinese : english); string result = (UseChinese ? english : chinese); if (!string.IsNullOrWhiteSpace(text)) { return text; } return result; } public static string Upper(string text) { if (!UseChinese) { return text.ToUpperInvariant(); } return text; } private static void RefreshIfNeeded() { DateTime utcNow = DateTime.UtcNow; if (!(utcNow < s_nextRefreshAtUtc)) { s_nextRefreshAtUtc = utcNow.AddSeconds(0.5); s_cachedLanguage = DetectLanguage(out s_cachedGameLanguageName); } } private static ScoreboardTextLanguage DetectLanguage(out string languageName) { //IL_0016: 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) if (TryReadGameLanguageName(out languageName)) { if (!IsChineseLanguageName(languageName)) { return ScoreboardTextLanguage.English; } return ScoreboardTextLanguage.Chinese; } try { languageName = ((object)Application.systemLanguage/*cast due to .constrained prefix*/).ToString(); return IsChineseLanguageName(languageName) ? ScoreboardTextLanguage.Chinese : ScoreboardTextLanguage.English; } catch { languageName = "Unknown"; return ScoreboardTextLanguage.English; } } private static bool TryReadGameLanguageName(out string languageName) { languageName = string.Empty; try { Type type = FindType("Localization.Text"); if (type != null) { if (TryReadCurrentLanguageName(ReadStaticMember(type, "TextLocalizationService"), out languageName)) { return true; } if (TryReadCurrentLanguageName(type, out languageName)) { return true; } } Type type2 = FindType("Localization.GameDataTextLocalizationService"); if (TryReadCurrentLanguageName((type2 == null) ? null : ReadStaticMember(type2, "Instance"), out languageName)) { return true; } return type2 != null && TryReadCurrentLanguageName(type2, out languageName); } catch { languageName = string.Empty; return false; } } private static bool TryReadCurrentLanguageName(object? service, out string languageName) { languageName = string.Empty; if (service == null) { return false; } object obj = ReadInstanceMember(service.GetType(), service, "CurrentLanguage"); if (obj == null) { return false; } languageName = obj.ToString() ?? string.Empty; return !string.IsNullOrWhiteSpace(languageName); } private static bool TryReadCurrentLanguageName(Type type, out string languageName) { string[] array = new string[4] { "CurrentLanguage", "SelectedLanguage", "CurrentLanguageName", "Language" }; foreach (string name in array) { languageName = ReadStaticMember(type, name)?.ToString() ?? string.Empty; if (!string.IsNullOrWhiteSpace(languageName)) { return true; } } languageName = string.Empty; return false; } private static object? ReadStaticMember(Type type, string name) { object obj = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); if (obj == null) { obj = type.GetMethod("get_" + name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(null, null); if (obj == null) { FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if ((object)field == null) { return null; } obj = field.GetValue(null); } } return obj; } private static object? ReadInstanceMember(Type type, object instance, string name) { object obj = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance); if (obj == null) { obj = type.GetMethod("get_" + name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(instance, null); if (obj == null) { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)field == null) { return null; } obj = field.GetValue(instance); } } return obj; } private static Type? FindType(string fullName) { Type type = Type.GetType(fullName, throwOnError: false); if (type != null) { return type; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { type = assembly.GetType(fullName, throwOnError: false); if (type != null) { return type; } } catch { } } return null; } private static bool IsChineseLanguageName(string languageName) { if (languageName.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) < 0 && languageName.IndexOf("Simplified", StringComparison.OrdinalIgnoreCase) < 0 && languageName.IndexOf("Traditional", StringComparison.OrdinalIgnoreCase) < 0 && languageName.IndexOf("zh", StringComparison.OrdinalIgnoreCase) < 0 && languageName.IndexOf("China", StringComparison.OrdinalIgnoreCase) < 0) { return languageName.IndexOf("Taiwan", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } } public enum ScoreboardTextLanguage { English, Chinese } } namespace KainAdvancedScoreboard.Core { [BepInPlugin("kain.gtfo.advanced_scoreboard", "Kain_Advanced_Scoreboard", "0.1.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BasePlugin { private ScoreboardConfig? _config; private ScoreboardRuntime? _runtime; private ScoreboardNativeCallbacks? _nativeCallbacks; private ScoreboardNetwork? _network; private ScoreboardPanel? _panel; private Harmony? _harmony; private ScoreboardRuntimeDriver? _driver; private GameObject? _driverHost; private bool _wasInExpedition; private bool _autoShownForLastExit; private bool _runtimeUpdateSeen; private bool _fallbackVisible; private bool _fallbackPanelWarningLogged; private bool _nativeCallbacksSkippedLogged; private Vector2 _fallbackScroll; private float _nextLifecycleCheckAt; private float _nextNetworkRegisterAt; private float _nextSyncAt; public static Plugin? Instance { get; private set; } public ScoreboardRuntime? Runtime => _runtime; public override void Load() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) Instance = this; ScoreboardLogger.Bind(((BasePlugin)this).Log); ScoreboardLogger.Info("Kain_Advanced_Scoreboard 0.1.3 loading."); _config = new ScoreboardConfig(((BasePlugin)this).Config); _runtime = new ScoreboardRuntime(() => _config?.VerboseEventLog.Value ?? false); _nativeCallbacks = new ScoreboardNativeCallbacks(_runtime); _network = new ScoreboardNetwork(_runtime); RegisterRuntimeDriver(); PatchGameplayHooks(); InstallNativeCallbacks(); ScoreboardLogger.Info(string.Format("{0} loaded. Toggle={1}, Host-authoritative summaries enabled.", "Kain_Advanced_Scoreboard", _config.ToggleHotkey.Value)); } public void OnRuntimeDriverStarted(ScoreboardRuntimeDriver driver) { ScoreboardLogger.Info("KAS runtime driver started."); } public void OnRuntimeUpdate() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) try { float time = Time.time; if (!_runtimeUpdateSeen) { _runtimeUpdateSeen = true; ScoreboardLogger.Info("KAS runtime driver Update is active."); } TryRegisterNetwork(); _runtime?.SamplePlayerLifeStates(); if (time >= _nextLifecycleCheckAt) { _nextLifecycleCheckAt = time + 0.5f; UpdateExpeditionLifecycle(); } if (_config != null && Input.GetKeyDown(_config.ToggleHotkey.Value)) { ToggleScoreboard(); } if (_fallbackVisible && Input.GetKeyDown((KeyCode)27)) { _fallbackVisible = false; } bool flag = _panel != null && _panel.Visible; if (flag) { _panel.Tick(); } if (_wasInExpedition || flag || _fallbackVisible) { TrySyncSummary(time, force: false); } } catch (Exception ex) { ScoreboardLogger.Error("KAS runtime update failed. Continuing.", ex); } } public void OnRuntimeLateUpdate() { ScoreboardPanel panel = _panel; if ((panel != null && panel.Visible) || _fallbackVisible) { Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } } public void OnRuntimeOnGUI() { if (_fallbackVisible) { DrawFallbackScoreboard(); } } public void OnRuntimeDriverDestroyed(ScoreboardRuntimeDriver driver) { //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_0016: Expected O, but got Unknown //IL_0016: Expected O, but got Unknown if ((Object)_driver == (Object)driver) { _driver = null; _driverHost = null; _runtimeUpdateSeen = false; } ScoreboardLogger.Warning("KAS runtime driver was destroyed."); } public static void RegisterPlayer(PlayerAgent? player) { Instance?._runtime?.RegisterPlayer(player); } public static void OnBulletWeaponFired(BulletWeapon? weapon) { Instance?._runtime?.OnBulletWeaponFired(weapon); } public static void OnSyncedBulletsFired(PlayerSync? sync, int count) { Instance?._runtime?.OnSyncedBulletsFired(sync, count); } public static void OnPlayerInventorySyncedGetSync(PlayerInventorySynced? inventory) { Instance?._runtime?.OnPlayerInventorySyncedGetSync(inventory); } public static void OnEnemyDamaged(Dam_EnemyDamageBase? damageBase, Agent? sourceAgent, float damage, int limbId, bool isMelee, bool wasSleepingTarget) { Instance?._runtime?.OnEnemyDamaged(damageBase, sourceAgent, damage, limbId, isMelee, wasSleepingTarget); } public static void OnEnemyBulletDamageReceived(Dam_EnemyDamageBase? damageBase, pBulletDamageData data) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) Instance?._runtime?.OnEnemyBulletDamageReceived(damageBase, data); } public static void OnEnemyMeleeDamageReceived(Dam_EnemyDamageBase? damageBase, pFullDamageData data) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) Instance?._runtime?.OnEnemyMeleeDamageReceived(damageBase, data); } public static void OnEnemyTakeDamage(EnemyAgent? enemy, float damage, Agent? sourceAgent) { Instance?._runtime?.OnEnemyTakeDamage(enemy, damage, sourceAgent); } public static void OnEnemyDead(EnemyAgent? enemy) { Instance?._runtime?.OnEnemyDead(enemy); } public static void OnEnemyTagged(EnemyAgent? enemy) { Instance?._runtime?.OnEnemyTagged(enemy); } public static void OnPlayerDamage(PlayerAgent? player, float damage, string reason) { Instance?._runtime?.OnPlayerDamage(player, damage, reason); } public static void OnIncomingPlayerDamage(PlayerAgent? player, float damage, float originalDamage, Agent? sourceAgent) { Instance?._runtime?.OnIncomingPlayerDamage(player, damage, originalDamage, sourceAgent); } public static void OnGiveHealth(PlayerAgent? receiver, PlayerAgent? giver, float amountRel) { Instance?._runtime?.OnResourceReceived(receiver, giver, ResourcePackKind.Medical); } public static void OnGiveDisinfection(PlayerAgent? receiver, PlayerAgent? giver, float amountRel) { Instance?._runtime?.OnResourceReceived(receiver, giver, ResourcePackKind.Disinfection); } public static void OnGiveAmmo(PlayerAgent? receiver, PlayerAgent? giver, float standardRel, float specialRel, float toolRel) { if (standardRel > 0f || specialRel > 0f) { Instance?._runtime?.OnResourceReceived(receiver, giver, ResourcePackKind.Ammo); } if (toolRel > 0f) { Instance?._runtime?.OnResourceReceived(receiver, giver, ResourcePackKind.Tool); } } public static void OnReviveSetup(Interact_Revive? interaction, PlayerAgent? owner) { Instance?._runtime?.OnReviveSetup(interaction, owner); } public static void OnReviveCompleted(Interact_Revive? interaction, PlayerAgent? source) { Instance?._runtime?.OnReviveCompleted(interaction, source); } public static void OnBioscanUpdated(CP_Bioscan_Core? core) { Instance?._runtime?.OnBioscanUpdated(core); } public static void OnTerminalCommand(object? terminalOrInterpreter) { Instance?._runtime?.OnTerminalCommand(terminalOrInterpreter); } public static void OnObjectivePickup(object? pickup, PlayerAgent? player) { Instance?._runtime?.OnObjectivePickup(pickup, player); } public static void InstallNativeCallbacks() { Plugin instance = Instance; if (instance != null && !instance._nativeCallbacksSkippedLogged) { instance._nativeCallbacksSkippedLogged = true; ScoreboardLogger.Info("KAS native GTFO callbacks skipped; Harmony gameplay hooks provide scoreboard counters."); } } private void ToggleScoreboard() { ScoreboardPanel panel = _panel; if ((panel != null && panel.Visible) || _fallbackVisible) { _panel?.Hide(); _fallbackVisible = false; ScoreboardLogger.Info("KAS scoreboard hidden by hotkey."); return; } EnsureUiCreated(); TrySyncSummary(Time.time, force: true); if (_panel != null) { _panel.Show(_runtime?.CreateDisplaySnapshot() ?? ScoreboardSnapshot.Empty); _fallbackVisible = false; ScoreboardLogger.Info("KAS scoreboard shown by hotkey."); } else { ShowFallbackScoreboard("hotkey"); } } private void ShowScoreboardForSettlement() { EnsureUiCreated(); TrySyncSummary(Time.time, force: true); if (_panel != null) { _panel.Show(_runtime?.CreateDisplaySnapshot() ?? ScoreboardSnapshot.Empty); _fallbackVisible = false; ScoreboardLogger.Info("KAS scoreboard shown for expedition end."); } else { ShowFallbackScoreboard("expedition end"); } } private void TrySyncSummary(float now, bool force) { if (_network == null || _runtime == null || _config == null) { return; } if (!_network.TryRegister()) { if (_panel != null && _panel.Visible) { _panel.Refresh(_runtime.CreateDisplaySnapshot()); } return; } float num = Mathf.Max(1f, _config.SyncIntervalSeconds.Value); if (force || !(now < _nextSyncAt)) { _nextSyncAt = now + num; _network.SyncSummary(force); if (_panel != null && _panel.Visible) { _panel.Refresh(_runtime.CreateDisplaySnapshot()); } } } private void TryRegisterNetwork() { float time = Time.time; if (time < _nextNetworkRegisterAt) { return; } _nextNetworkRegisterAt = time + 2f; try { _network?.TryRegister(); } catch (Exception ex) { ScoreboardLogger.Error("KAS network registration tick failed. Continuing with local scoreboard.", ex); } } private void UpdateExpeditionLifecycle() { if (IsExpeditionInLevel()) { if (!_wasInExpedition) { _runtime?.ResetForNewExpedition(); _autoShownForLastExit = false; ScoreboardLogger.Info("KAS expedition stats started."); } _wasInExpedition = true; } else if (_wasInExpedition) { _wasInExpedition = false; _runtime?.MarkExpeditionEnded(); TrySyncSummary(Time.time, force: true); ScoreboardConfig config = _config; if (config != null && config.AutoShowOnExpeditionEnd.Value && !_autoShownForLastExit) { _autoShownForLastExit = true; ShowScoreboardForSettlement(); } } } private static bool IsExpeditionInLevel() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 try { return (int)GameStateManager.CurrentStateName == 10 && GameStateManager.IsInExpedition && Time.time - GS_InLevel.EnterTime >= 1.5f; } catch { return false; } } private void RegisterRuntimeDriver() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown ScoreboardRuntimeDriver.Plugin = this; if (!((Object)_driver != (Object)null)) { _driver = ((BasePlugin)this).AddComponent(); _driverHost = (((Object)_driver != (Object)null) ? ((Component)_driver).gameObject : null); if ((Object)_driver != (Object)null) { ScoreboardLogger.Info("Registered KAS runtime driver through BepInEx IL2CPP component host."); } else { ScoreboardLogger.Warning("KAS runtime driver registration returned null; hotkey UI will not update until a driver is available."); } } } private void PatchGameplayHooks() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown try { _harmony = new Harmony("kain.gtfo.advanced_scoreboard"); int num = 0; int num2 = 0; foreach (Type item in from type in typeof(ScoreboardGameplayPatches).Assembly.GetTypes() where type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0 select type) { try { _harmony.PatchAll(item); num++; } catch (Exception ex) { num2++; ScoreboardLogger.Error("Failed to install KAS patch group " + item.FullName + ". Continuing.", ex); } } ScoreboardLogger.Info($"KAS patch groups installed. Success={num} Failed={num2}."); } catch (Exception ex2) { ScoreboardLogger.Error("Failed to install KAS gameplay hooks.", ex2); } } private void EnsureUiCreated() { if (_panel != null) { return; } try { _panel = new ScoreboardPanel(() => _runtime?.CreateDisplaySnapshot() ?? ScoreboardSnapshot.Empty); _panel.Create(); } catch (Exception ex) { _panel = null; ScoreboardLogger.Error("KAS scoreboard UI creation failed. It will retry next time the scoreboard is opened.", ex); } } private void ShowFallbackScoreboard(string reason) { _fallbackVisible = true; if (!_fallbackPanelWarningLogged) { _fallbackPanelWarningLogged = true; ScoreboardLogger.Warning("KAS fallback IMGUI scoreboard opened from " + reason + "; the Unity UI panel was not available."); } } private void DrawFallbackScoreboard() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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_018e: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) try { ScoreboardSnapshot scoreboardSnapshot = _runtime?.CreateDisplaySnapshot() ?? ScoreboardSnapshot.Empty; float num = Mathf.Min((float)Screen.width - 40f, 1180f); float num2 = Mathf.Min((float)Screen.height - 40f, 760f); num = Mathf.Max(num, 520f); num2 = Mathf.Max(num2, 360f); float num3 = ((float)Screen.width - num) * 0.5f; float num4 = 24f; GUI.Box(new Rect(num3, num4, num, num2), string.Empty); GUI.Label(new Rect(num3 + 18f, num4 + 12f, num - 36f, 24f), ScoreboardLocalization.Title + " | " + ScoreboardLocalization.CloseHint); GUI.Label(new Rect(num3 + 18f, num4 + 34f, num - 36f, 22f), scoreboardSnapshot.IsHostAuthoritative ? ScoreboardLocalization.HostSummary : ScoreboardLocalization.LocalSummary); object obj = ((scoreboardSnapshot.Players.Count != 0) ? ((object)scoreboardSnapshot.Players.ToArray()) : ((object)new PlayerScoreStats[1] { new PlayerScoreStats { PlayerName = ScoreboardLocalization.NoStats } })); float num5 = num - 62f; float num6 = 172f; float num7 = Mathf.Max((float)((Array)obj).Length * (num6 + 10f) + 8f, num2 - 90f); _fallbackScroll = GUI.BeginScrollView(new Rect(num3 + 18f, num4 + 62f, num - 36f, num2 - 78f), _fallbackScroll, new Rect(0f, 0f, num5, num7)); float num8 = 0f; PlayerScoreStats[] array = (PlayerScoreStats[])obj; for (int i = 0; i < array.Length; i++) { DrawFallbackPlayerBlock(array[i], scoreboardSnapshot.HasObjectivePickups, num5, num8, num6); num8 += num6 + 10f; } GUI.EndScrollView(); } catch (Exception ex) { _fallbackVisible = false; ScoreboardLogger.Error("KAS fallback scoreboard failed and was hidden.", ex); } } private static void DrawFallbackPlayerBlock(PlayerScoreStats player, bool showObjectivePickups, float width, float y, float height) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0419: Unknown result type (might be due to invalid IL or missing references) //IL_043d: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Unknown result type (might be due to invalid IL or missing references) GUI.Box(new Rect(0f, y, width, height), string.Empty); string text = (string.IsNullOrWhiteSpace(player.PlayerName) ? "Player" : player.PlayerName); GUI.Label(new Rect(12f, y + 8f, width - 24f, 22f), text); string text2 = string.Join("\n", string.Format("{0}: {1} / {2}", ScoreboardLocalization.Pick("Small/Large kills", "小型/大型击杀"), player.SmallKills, player.LargeKills), string.Format("{0}: {1} / {2}", ScoreboardLocalization.Pick("Scout/Boss kills", "Scout/Boss 击杀"), player.ScoutKills, player.BossKills), ScoreboardLocalization.Pick("Total damage", "总伤害") + ": " + FormatNumber(player.TotalDamage), ScoreboardLocalization.Pick("Damage taken", "承受伤害") + ": " + FormatNumber(player.DamageTaken), string.Format("{0}: {1} / {2}", ScoreboardLocalization.Pick("Downed/Rescued", "倒地/救援"), player.DownedCount, player.RescueCount), string.Format("{0}: {1} / {2}", ScoreboardLocalization.Pick("Melee/Tool kills", "近战/工具击杀"), player.MeleeKills, player.ToolKills)); string text3 = string.Join("\n", string.Format("{0}: {1} / {2}", ScoreboardLocalization.Pick("Primary shots/hits", "主武器开火/命中"), player.PrimaryWeapon.ShotsFired, player.PrimaryWeapon.Hits), string.Format("{0}: {1}", ScoreboardLocalization.Pick("Primary weakspots", "主武器弱点"), player.PrimaryWeapon.WeakspotHits), ScoreboardLocalization.Pick("Primary hit/weak rate", "主武器命中/弱点率") + ": " + FormatPercent(player.PrimaryWeapon.HitRate) + " / " + FormatPercent(player.PrimaryWeapon.WeakspotRate), string.Format("{0}: {1} / {2}", ScoreboardLocalization.Pick("Secondary shots/hits", "副武器开火/命中"), player.SecondaryWeapon.ShotsFired, player.SecondaryWeapon.Hits), string.Format("{0}: {1}", ScoreboardLocalization.Pick("Secondary weakspots", "副武器弱点"), player.SecondaryWeapon.WeakspotHits), ScoreboardLocalization.Pick("Secondary hit/weak rate", "副武器命中/弱点率") + ": " + FormatPercent(player.SecondaryWeapon.HitRate) + " / " + FormatPercent(player.SecondaryWeapon.WeakspotRate)); string text4 = string.Join("\n", string.Format("{0}: {1} / {2}", ScoreboardLocalization.Pick("Med/Ammo packs", "医疗/弹药包"), player.MedicalPacksReceived, player.AmmoPacksReceived), string.Format("{0}: {1} / {2}", ScoreboardLocalization.Pick("Tool/Disinfect packs", "工具/消毒包"), player.ToolPacksReceived, player.DisinfectionPacksReceived), ScoreboardLocalization.Pick("Damage per ammo pack", "每份弹药包伤害") + ": " + FormatDamagePerAmmo(player.DamagePerAmmoPack), ScoreboardLocalization.Pick("Scan time", "扫描参与时间") + ": " + FormatSeconds(player.ScanParticipationSeconds), string.Format("{0}: {1}", ScoreboardLocalization.Pick("Stealth kills", "潜行击杀"), player.StealthKills)); float num = (width - 48f) / 3f; GUI.Label(new Rect(12f, y + 34f, num, height - 42f), text2); GUI.Label(new Rect(24f + num, y + 34f, num, height - 42f), text3); GUI.Label(new Rect(36f + num * 2f, y + 34f, num, height - 42f), text4); } private static string FormatNumber(float value) { if (!(value >= 1000f)) { return value.ToString("0.#", CultureInfo.InvariantCulture); } return value.ToString("0", CultureInfo.InvariantCulture); } private static string FormatPercent(float value) { return (value * 100f).ToString("0.#", CultureInfo.InvariantCulture) + "%"; } private static string FormatSeconds(float value) { return value.ToString("0.0", CultureInfo.InvariantCulture) + "s"; } private static string FormatDamagePerAmmo(float value) { if (!float.IsPositiveInfinity(value)) { return FormatNumber(value); } return ScoreboardLocalization.Pick("Infinity", "无穷大"); } } public static class PluginInfo { public const string Guid = "kain.gtfo.advanced_scoreboard"; public const string Name = "Kain_Advanced_Scoreboard"; public const string ShortName = "KAS"; public const string Version = "0.1.3"; } public sealed class ScoreboardConfig { public ConfigEntry ToggleHotkey { get; } public ConfigEntry AutoShowOnExpeditionEnd { get; } public ConfigEntry SyncIntervalSeconds { get; } public ConfigEntry VerboseEventLog { get; } public ScoreboardConfig(ConfigFile config) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 ToggleHotkey = config.Bind("UI", "ToggleHotkey", (KeyCode)96, "Toggle the advanced scoreboard."); if ((int)ToggleHotkey.Value == 291 || (int)ToggleHotkey.Value == 107) { ToggleHotkey.Value = (KeyCode)96; } AutoShowOnExpeditionEnd = config.Bind("UI", "AutoShowOnExpeditionEnd", true, "Show the scoreboard when the expedition leaves InLevel state."); SyncIntervalSeconds = config.Bind("Network", "SyncIntervalSeconds", 1f, "Low-frequency summary sync interval while an expedition is active."); if (Mathf.Approximately(SyncIntervalSeconds.Value, 4f)) { SyncIntervalSeconds.Value = 1f; } VerboseEventLog = config.Bind("Diagnostics", "VerboseEventLog", false, "Log individual stat events. Keep false for normal play."); } } public static class ScoreboardLogger { private static ManualLogSource? s_log; public static void Bind(ManualLogSource log) { s_log = log; } public static void Info(string message) { ManualLogSource val = s_log; if (val != null) { val.LogInfo((object)message); } } public static void Warning(string message) { ManualLogSource val = s_log; if (val != null) { val.LogWarning((object)message); } } public static void Error(string message, Exception? ex = null) { ManualLogSource val = s_log; if (val != null) { val.LogError((object)((ex == null) ? message : (message + "\n" + ex))); } } } public sealed class ScoreboardRuntimeDriver : MonoBehaviour { public static Plugin? Plugin { get; set; } public ScoreboardRuntimeDriver(IntPtr ptr) : base(ptr) { } public void Start() { Plugin?.OnRuntimeDriverStarted(this); } public void Update() { Plugin?.OnRuntimeUpdate(); } public void LateUpdate() { Plugin?.OnRuntimeLateUpdate(); } public void OnGUI() { Plugin?.OnRuntimeOnGUI(); } public void OnDestroy() { Plugin?.OnRuntimeDriverDestroyed(this); } } }