using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using IronNestStats.Core.Formatting; using IronNestStats.Core.Models; using IronNestStats.Core.Stats; using IronNestStats.Melon; using IronNestStats.Melon.Configuration; using IronNestStats.Melon.Game; using IronNestStats.Melon.Runtime; using IronNestStats.Melon.UI; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Plugin), "MissionStats", "0.1.0", "rvql", null)] [assembly: MelonGame("Iron Nest", "Iron Nest Heavy Turret Simulator")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace IronNestStats.Melon { public sealed class Plugin : MelonMod { private ModRuntime _runtime; public override void OnInitializeMelon() { ModSettings settings = new ModSettings(); _runtime = new ModRuntime(settings, delegate(string message) { ((MelonBase)this).LoggerInstance.Msg(message); }, delegate(string message) { ((MelonBase)this).LoggerInstance.Warning(message); }); ((MelonBase)this).LoggerInstance.Msg("MissionStats loaded. No native game-method hooks are used."); } public override void OnUpdate() { if (_runtime != null) { _runtime.Update(); } } public override void OnGUI() { if (_runtime != null) { _runtime.Draw(); } } public override void OnDeinitializeMelon() { if (_runtime != null) { _runtime.Shutdown(); } _runtime = null; } } } namespace IronNestStats.Melon.UI { internal sealed class OverlayRenderer { private sealed class MedalPanelLayout { public float PanelWidth { get; } public float HeaderHeight { get; } public float MissionTitleHeight { get; } public float ContentHeight { get; } public IList MedalTitleHeights { get; } public IList RelatedStatisticHeights { get; } public ConditionTableLayout ConditionTable { get; } public MedalPanelLayout(float panelWidth, float headerHeight, float missionTitleHeight, float contentHeight, IList medalTitleHeights, IList relatedStatisticHeights, ConditionTableLayout conditionTable) { PanelWidth = panelWidth; HeaderHeight = headerHeight; MissionTitleHeight = missionTitleHeight; ContentHeight = contentHeight; MedalTitleHeights = medalTitleHeights; RelatedStatisticHeights = relatedStatisticHeights; ConditionTable = conditionTable; } } private sealed class ConditionTableLayout { public float LabelWidth { get; } public float[] TierWidths { get; } public float TotalWidth { get; } public ConditionTableLayout(float labelWidth, float[] tierWidths, float totalWidth) { LabelWidth = labelWidth; TierWidths = tierWidths; TotalWidth = totalWidth; } } private sealed class ConditionTableRow { public string Label { get; } public string[] TargetValues { get; } public ConditionTableRow(string label) { Label = label; TargetValues = new string[3]; } } private sealed class StatRow { public string Label { get; } public string Value { get; } public StatRow(string label, string value) { Label = label; Value = value; } } private const float ProgressBarWidth = 240f; private GUIStyle _panelTitle; private GUIStyle _missionTitle; private GUIStyle _medalName; private GUIStyle _conditionText; private GUIStyle _relatedStats; private GUIStyle _tableHeader; private GUIStyle _conditionValue; private GUIStyle _statLabel; private GUIStyle _statValue; private GUIStyle _tickLabel; public void Draw(StatsSnapshot snapshot, ModSettings settings, bool showMedals, bool showStats, GameLocalizationService localization) { EnsureStyles(); MedalPanelLayout medalPanelLayout = null; if (showMedals) { medalPanelLayout = CreateMedalPanelLayout(snapshot, settings, localization); DrawMedalPanel(snapshot, settings, localization, medalPanelLayout); } if (showStats) { DrawStatisticsPanel(snapshot, settings, localization, medalPanelLayout?.PanelWidth ?? 0f); } } private void DrawMedalPanel(StatsSnapshot snapshot, ModSettings settings, GameLocalizationService localization, MedalPanelLayout layout) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_0172: 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_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) bool flag = localization.IsChinese(snapshot.Language); float panelWidth = layout.PanelWidth; float val = Math.Min(Math.Max(320f, settings.PanelHeight.Value), (float)Screen.height - 32f); float num = Math.Min(Math.Max(220f, layout.ContentHeight), val); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor((float)Screen.width - panelWidth - 16f, 16f, panelWidth, num); if (IsPointerInside(val2)) { DrawBackground(val2, settings.Opacity); } GUI.Label(new Rect(((Rect)(ref val2)).x + 16f, ((Rect)(ref val2)).y + 10f, ((Rect)(ref val2)).width - 32f, 20f), flag ? "任务" : "MISSION", _panelTitle); GUI.Label(new Rect(((Rect)(ref val2)).x + 16f, ((Rect)(ref val2)).y + 30f, ((Rect)(ref val2)).width - 32f, layout.MissionTitleHeight), snapshot.MissionName, _missionTitle); float num2 = ((Rect)(ref val2)).y + layout.HeaderHeight; MedalSnapshot val3 = null; Rect val5 = default(Rect); for (int i = 0; i < snapshot.Medals.Count; i++) { MedalSnapshot val4 = snapshot.Medals[i]; float num3 = num2; float num4 = layout.MedalTitleHeights[i]; GUI.Label(new Rect(((Rect)(ref val2)).x + 16f, num2, ((Rect)(ref val2)).width - 32f, num4), val4.Name, _medalName); num2 += num4 + 2f; ((Rect)(ref val5))..ctor(((Rect)(ref val2)).right - 16f - 240f, num2, 240f, 18f); DrawSolid(val5, new Color(0.13f, 0.16f, 0.2f, 0.95f)); DrawSolid(new Rect(((Rect)(ref val5)).x + 2f, ((Rect)(ref val5)).y + 2f, Math.Max(0f, (((Rect)(ref val5)).width - 4f) * (float)val4.Progress), ((Rect)(ref val5)).height - 4f), ProgressColor(val4.Progress)); DrawTierTicks(val5, snapshot.Language, localization); num2 += 21f; string text = BuildRelatedStatistics(val4, snapshot.Language, localization); float num5 = layout.RelatedStatisticHeights[i]; GUI.Label(new Rect(((Rect)(ref val2)).x + 16f, num2, ((Rect)(ref val2)).width - 32f, num5), text, _relatedStats); num2 += num5 + 2f; num2 += 7f; if (IsPointerInside(new Rect(((Rect)(ref val2)).x + 16f, num3, ((Rect)(ref val2)).width - 32f, num2 - num3))) { val3 = val4; } if (num2 > ((Rect)(ref val2)).bottom - 20f) { break; } } if (val3 != null && GetConditionRowCount(val3) > 0 && num2 < ((Rect)(ref val2)).bottom - 40f) { GUI.Label(new Rect(((Rect)(ref val2)).x + 16f, num2, ((Rect)(ref val2)).width - 32f, 22f), val3.Name, _medalName); DrawConditionTable(val2, num2 + 24f, val3, snapshot.Language, localization, layout.ConditionTable); } } private MedalPanelLayout CreateMedalPanelLayout(StatsSnapshot snapshot, ModSettings settings, GameLocalizationService localization) { float num = Math.Min(Math.Max(380f, settings.MedalPanelWidth.Value), (float)Screen.width - 32f) - 32f; ConditionTableLayout conditionTableLayout = CreateConditionTableLayout(snapshot.Medals, snapshot.Language, localization, 240f, num); float val = Math.Max(240f, conditionTableLayout.TotalWidth); val = Math.Max(val, MeasureWidth(_missionTitle, snapshot.MissionName) + 4f); foreach (MedalSnapshot medal in snapshot.Medals) { val = Math.Max(val, MeasureWidth(_medalName, medal.Name) + 4f); } val = Math.Min(val, num); float panelWidth = val + 32f; float num2 = Math.Max(32f, MeasureHeight(_missionTitle, snapshot.MissionName, val)); float num3 = 30f + num2 + 8f; List list = new List(); List list2 = new List(); float num4 = 0f; foreach (MedalSnapshot medal2 in snapshot.Medals) { float num5 = Math.Max(22f, MeasureHeight(_medalName, medal2.Name, val)); string value = BuildRelatedStatistics(medal2, snapshot.Language, localization); float num6 = Math.Max(20f, MeasureHeight(_relatedStats, value, val)); list.Add(num5); list2.Add(num6); num4 += num5 + num6 + 32f; } int maximumConditionRows = GetMaximumConditionRows(snapshot.Medals); float num7 = ((maximumConditionRows == 0) ? 0f : (56f + (float)maximumConditionRows * 34f)); return new MedalPanelLayout(panelWidth, num3, num2, num3 + num4 + num7, list, list2, conditionTableLayout); } private ConditionTableLayout CreateConditionTableLayout(IList medals, string language, GameLocalizationService localization, float minimumWidth, float maximumWidth) { float num = 120f; float[] array = new float[3] { 40f, 40f, 40f }; for (int i = 0; i < 3; i++) { array[i] = Math.Max(array[i], MeasureWidth(_tableHeader, localization.GetTierLabel(i switch { 1 => "Silver", 0 => "Bronze", _ => "Gold", }, language)) + 16f); } foreach (MedalSnapshot medal in medals) { foreach (ConditionTableRow item in BuildConditionRows(medal, language, localization)) { num = Math.Max(num, MeasureWidth(_conditionText, item.Label) + 18f); for (int j = 0; j < 3; j++) { array[j] = Math.Max(array[j], MeasureWidth(_conditionValue, item.TargetValues[j]) + 16f); } } } float num2 = array[0] + array[1] + array[2]; float num3 = num + num2; if (num3 > maximumWidth) { num = Math.Max(120f, maximumWidth - num2); num3 = num + num2; if (num3 > maximumWidth) { float val = Math.Max(40f, (maximumWidth - 120f) / 3f); for (int k = 0; k < 3; k++) { array[k] = Math.Min(array[k], val); } num2 = array[0] + array[1] + array[2]; num = Math.Max(120f, maximumWidth - num2); num3 = num + num2; } } if (num3 < minimumWidth) { num += minimumWidth - num3; num3 = minimumWidth; } return new ConditionTableLayout(num, array, Math.Min(num3, maximumWidth)); } private static float MeasureWidth(GUIStyle style, string value) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(value)) { return 0f; } return style.CalcSize(new GUIContent(value)).x; } private static float MeasureHeight(GUIStyle style, string value, float width) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown if (string.IsNullOrEmpty(value)) { return 0f; } return style.CalcHeight(new GUIContent(value), width); } private string BuildRelatedStatistics(MedalSnapshot medal, string language, GameLocalizationService localization) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (MedalTierSnapshot tier in medal.Tiers) { foreach (MedalConditionSnapshot condition in tier.Conditions) { if (condition.InputValues.Count > 0) { foreach (KeyValuePair inputValue in condition.InputValues) { dictionary[inputValue.Key] = inputValue.Value; } } else { dictionary[condition.StatisticId] = condition.CurrentValue; } } } List list = new List(); foreach (KeyValuePair item in dictionary) { list.Add(localization.GetStatLabel(item.Key, language) + " " + item.Value.ToString("0.##", CultureInfo.InvariantCulture)); } return string.Join(" · ", list); } private float DrawConditionTable(Rect panel, float y, MedalSnapshot medal, string language, GameLocalizationService localization, ConditionTableLayout layout) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: 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_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) IList list = BuildConditionRows(medal, language, localization); if (list.Count == 0) { return y; } float totalWidth = layout.TotalWidth; float num = ((Rect)(ref panel)).right - 16f - totalWidth; float labelWidth = layout.LabelWidth; float num2 = 22f + (float)list.Count * 34f; Color color = default(Color); ((Color)(ref color))..ctor(0.4f, 0.52f, 0.6f, 0.55f); DrawSolid(new Rect(num, y, totalWidth, 1f), color); DrawSolid(new Rect(num, y + 22f, totalWidth, 1f), color); DrawSolid(new Rect(num, y, 1f, num2), color); DrawSolid(new Rect(num + labelWidth, y, 1f, num2), color); for (int i = 1; i <= 3; i++) { DrawSolid(new Rect(num + labelWidth + SumTierWidths(layout, i), y, 1f, num2), color); } for (int j = 0; j < 3; j++) { GUI.Label(new Rect(num + labelWidth + SumTierWidths(layout, j) + 5f, y + 1f, layout.TierWidths[j] - 10f, 20f), localization.GetTierLabel(j switch { 1 => "Silver", 0 => "Bronze", _ => "Gold", }, language), _tableHeader); } for (int k = 0; k < list.Count; k++) { float num3 = y + 22f + (float)k * 34f; DrawSolid(new Rect(num, num3 + 34f, totalWidth, 1f), color); GUI.Label(new Rect(num + 7f, num3 + 2f, labelWidth - 14f, 30f), list[k].Label, _conditionText); for (int l = 0; l < 3; l++) { GUI.Label(new Rect(num + labelWidth + SumTierWidths(layout, l) + 5f, num3 + 2f, layout.TierWidths[l] - 10f, 30f), list[k].TargetValues[l] ?? string.Empty, _conditionValue); } } return y + num2 + 10f; } private static MedalTierSnapshot FindTier(MedalSnapshot medal, string tierName) { foreach (MedalTierSnapshot tier in medal.Tiers) { if (string.Equals(tier.Tier, tierName, StringComparison.OrdinalIgnoreCase)) { return tier; } } return null; } private static IList BuildConditionRows(MedalSnapshot medal, string language, GameLocalizationService localization) { List list = new List(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < 3; i++) { MedalTierSnapshot val = FindTier(medal, i switch { 1 => "Silver", 0 => "Bronze", _ => "Gold", }); if (val == null) { continue; } Dictionary dictionary2 = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (MedalConditionSnapshot condition in val.Conditions) { string conditionBaseKey = GetConditionBaseKey(condition); dictionary2.TryGetValue(conditionBaseKey, out var value); dictionary2[conditionBaseKey] = value + 1; string key = conditionBaseKey + "\u001f" + value.ToString(CultureInfo.InvariantCulture); if (!dictionary.TryGetValue(key, out var value2)) { value2 = (dictionary[key] = new ConditionTableRow(localization.GetStatLabel(condition.StatisticId, language) + " " + DisplayComparison(condition.Comparison))); list.Add(value2); } value2.TargetValues[i] = FormatConditionNumber(condition.TargetValue); } } return list; } private static int GetConditionRowCount(MedalSnapshot medal) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (MedalTierSnapshot tier in medal.Tiers) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (MedalConditionSnapshot condition in tier.Conditions) { string conditionBaseKey = GetConditionBaseKey(condition); dictionary.TryGetValue(conditionBaseKey, out var value); dictionary[conditionBaseKey] = value + 1; hashSet.Add(conditionBaseKey + "\u001f" + value.ToString(CultureInfo.InvariantCulture)); } } return hashSet.Count; } private static string GetConditionBaseKey(MedalConditionSnapshot condition) { return (condition.StatisticId ?? "Value") + "\u001f" + (condition.Comparison ?? "?"); } private static int GetMaximumConditionRows(IList medals) { int num = 0; foreach (MedalSnapshot medal in medals) { num = Math.Max(num, GetConditionRowCount(medal)); } return num; } private static float SumTierWidths(ConditionTableLayout layout, int exclusiveEnd) { float num = 0f; for (int i = 0; i < exclusiveEnd; i++) { num += layout.TierWidths[i]; } return num; } private static string DisplayComparison(string comparison) { object obj; switch (comparison) { default: obj = comparison; if (obj == null) { return "?"; } break; case "!=": return "≠"; case "<=": return "≤"; case ">=": obj = "≥"; break; } return (string)obj; } private static string FormatConditionNumber(double value) { if (double.IsNaN(value)) { return "—"; } if (double.IsPositiveInfinity(value)) { return "∞"; } if (double.IsNegativeInfinity(value)) { return "-∞"; } return value.ToString("0.##", CultureInfo.InvariantCulture); } private void DrawStatisticsPanel(StatsSnapshot snapshot, ModSettings settings, GameLocalizationService localization, float medalPanelWidth) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0179: 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) bool flag = localization.IsChinese(snapshot.Language); float num = ((medalPanelWidth > 0f) ? (medalPanelWidth + 32f) : 0f); float num2 = Math.Min(Math.Max(640f, settings.StatsPanelWidth.Value), (float)Screen.width - num - 32f); if (num2 < 540f) { return; } float num3 = Math.Min(Math.Max(260f, settings.PanelHeight.Value), (float)Screen.height - 32f); Rect val = default(Rect); ((Rect)(ref val))..ctor(16f, 16f, num2, num3); DrawBackground(val, settings.Opacity); GUI.Label(new Rect(((Rect)(ref val)).x + 18f, ((Rect)(ref val)).y + 10f, ((Rect)(ref val)).width - 36f, 26f), flag ? "详细统计" : "DETAILED STATISTICS", _panelTitle); GUI.Label(new Rect(((Rect)(ref val)).x + 18f, ((Rect)(ref val)).y + 36f, ((Rect)(ref val)).width - 36f, 22f), snapshot.MissionName, _statLabel); List list = new List(); foreach (StatDefinition item in StatCatalog.All) { if (snapshot.Statistics.TryGetValue(item.Id, out var value)) { list.Add(new StatRow(localization.GetStatLabel(item.Id, snapshot.Language), ValueFormatter.Format(value, item.Format, flag))); } } DrawStatRows(val, list); } private void DrawStatRows(Rect panel, IList rows) { //IL_00b5: 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) float num = ((Rect)(ref panel)).height - 64f - 14f; int num2 = Math.Max(1, (int)(num / 21f)); int num3 = Math.Min(3, Math.Max(2, (int)Math.Ceiling((double)rows.Count / (double)num2))); float num4 = (((Rect)(ref panel)).width - 36f - 16f * (float)(num3 - 1)) / (float)num3; for (int i = 0; i < rows.Count && i < num2 * num3; i++) { int num5 = i / num2; int num6 = i % num2; float num7 = ((Rect)(ref panel)).x + 18f + (float)num5 * (num4 + 16f); float num8 = ((Rect)(ref panel)).y + 64f + (float)num6 * 21f; float num9 = num4 * 0.7f; GUI.Label(new Rect(num7, num8, num9, 21f), rows[i].Label, _statLabel); GUI.Label(new Rect(num7 + num9, num8, num4 - num9, 21f), rows[i].Value, _statValue); } } private static void DrawBackground(Rect rect, float opacity) { //IL_0000: 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_0035: 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) DrawSolid(rect, new Color(0.025f, 0.035f, 0.05f, opacity)); DrawSolid(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 3f, ((Rect)(ref rect)).height), new Color(0.22f, 0.62f, 0.86f, Math.Min(1f, opacity + 0.15f))); } private static bool IsPointerInside(Rect rect) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) Mouse current = Mouse.current; if (current != null) { Vector2 val = ((InputControl)(object)((Pointer)current).position).ReadValue(); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(val.x, (float)Screen.height - val.y); return ((Rect)(ref rect)).Contains(val2); } Event current2 = Event.current; if (current2 != null) { return ((Rect)(ref rect)).Contains(current2.mousePosition); } return false; } private static void DrawSolid(Rect rect, Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) Color color2 = GUI.color; GUI.color = color; GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture); GUI.color = color2; } private static Color ProgressColor(double progress) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (progress >= 1.0) { return new Color(0.88f, 0.69f, 0.2f, 1f); } if (progress >= 0.66) { return new Color(0.28f, 0.72f, 0.45f, 1f); } return new Color(0.2f, 0.55f, 0.82f, 1f); } private void DrawTierTicks(Rect bar, string language, GameLocalizationService localization) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_012e: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < 3; i++) { float num = ((float)i + 1f) / 3f; float num2 = ((Rect)(ref bar)).x + 2f + (((Rect)(ref bar)).width - 4f) * num; if (i == 2) { num2 = ((Rect)(ref bar)).right - 2f; } Color color = (Color)(i switch { 1 => new Color(0.76f, 0.8f, 0.84f, 1f), 0 => new Color(0.78f, 0.43f, 0.18f, 1f), _ => new Color(0.96f, 0.76f, 0.2f, 1f), }); DrawSolid(new Rect(num2 - 1f, ((Rect)(ref bar)).y + 1f, 2f, ((Rect)(ref bar)).height - 2f), color); string text = i switch { 1 => "Silver", 0 => "Bronze", _ => "Gold", }; string tierLabel = localization.GetTierLabel(text, language); string text2 = (string.IsNullOrEmpty(tierLabel) ? text.Substring(0, 1) : tierLabel.Substring(0, 1)); GUI.Label(new Rect(num2 - 22f, ((Rect)(ref bar)).y, 18f, ((Rect)(ref bar)).height), text2, _tickLabel); } } private void EnsureStyles() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0192: 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) if (_panelTitle == null) { _panelTitle = Style(13, (FontStyle)1, (TextAnchor)5, new Color(0.52f, 0.74f, 0.88f, 1f)); _missionTitle = Style(19, (FontStyle)1, (TextAnchor)5, new Color(0.96f, 0.97f, 0.99f, 1f)); _missionTitle.wordWrap = true; _medalName = Style(15, (FontStyle)1, (TextAnchor)5, new Color(0.93f, 0.94f, 0.96f, 1f)); _medalName.wordWrap = true; _conditionText = Style(10, (FontStyle)0, (TextAnchor)3, new Color(0.68f, 0.74f, 0.79f, 1f)); _conditionText.wordWrap = true; _relatedStats = Style(10, (FontStyle)0, (TextAnchor)5, new Color(0.7f, 0.78f, 0.84f, 1f)); _relatedStats.wordWrap = true; _tableHeader = Style(11, (FontStyle)1, (TextAnchor)4, new Color(0.88f, 0.91f, 0.94f, 1f)); _conditionValue = Style(11, (FontStyle)1, (TextAnchor)4, new Color(0.96f, 0.82f, 0.38f, 1f)); _statLabel = Style(13, (FontStyle)0, (TextAnchor)3, new Color(0.76f, 0.82f, 0.87f, 1f)); _statValue = Style(13, (FontStyle)1, (TextAnchor)5, new Color(0.96f, 0.82f, 0.38f, 1f)); _tickLabel = Style(9, (FontStyle)1, (TextAnchor)5, Color.white); } } private static GUIStyle Style(int size, FontStyle fontStyle, TextAnchor alignment, Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown GUIStyle val = new GUIStyle { fontSize = size, fontStyle = fontStyle, alignment = alignment }; val.normal.textColor = color; return val; } } } namespace IronNestStats.Melon.Runtime { internal sealed class ModRuntime { private readonly ModSettings _settings; private readonly Action _info; private readonly Action _warning; private readonly IronNestGameFacade _game; private readonly OverlayRenderer _renderer; private StatsSnapshot _snapshot; private bool _showMedals; private bool _showStats; private float _nextRefresh; private float _nextErrorLog; private bool _active = true; private bool _lastMissionActive; private string _lastMissionName = string.Empty; public ModRuntime(ModSettings settings, Action info, Action warning) { _settings = settings; _info = info; _warning = warning; _game = new IronNestGameFacade(); _renderer = new OverlayRenderer(); _snapshot = StatsSnapshot.Inactive("English"); _showMedals = settings.ShowMedalsByDefault.Value; _showStats = settings.ShowStatsByDefault.Value; } public void Update() { if (!_active) { return; } if (_settings.ToggleMedalsBinding.IsDown()) { _showMedals = !_showMedals; _info("Medal panel: " + (_showMedals ? "shown" : "hidden")); } if (_settings.ToggleStatsBinding.IsDown()) { _showStats = !_showStats; _info("Statistics panel: " + (_showStats ? "shown" : "hidden")); } if (Time.unscaledTime < _nextRefresh) { return; } _nextRefresh = Time.unscaledTime + _settings.RefreshSeconds; try { _snapshot = _game.Capture(_settings.MedalLimit, _settings.ShowMedalConditions.Value, _settings.ConditionLimit); if (_snapshot.Active != _lastMissionActive || (_snapshot.Active && _snapshot.MissionName != _lastMissionName)) { if (_snapshot.Active && !_lastMissionActive) { _showMedals = _settings.ShowMedalsByDefault.Value; _showStats = _settings.ShowStatsByDefault.Value; } _lastMissionActive = _snapshot.Active; _lastMissionName = _snapshot.MissionName; _info(_snapshot.Active ? ("Mission statistics active: " + _snapshot.MissionName) : "Mission statistics inactive"); } } catch (Exception ex) { _snapshot = StatsSnapshot.Inactive((_snapshot == null) ? "English" : _snapshot.Language); if (Time.unscaledTime >= _nextErrorLog) { _nextErrorLog = Time.unscaledTime + 5f; _warning("Statistics capture failed: " + ex); } } } public void Draw() { if (_active && _snapshot != null && _snapshot.Active && (_showMedals || _showStats)) { _renderer.Draw(_snapshot, _settings, _showMedals, _showStats, _game.Localization); } } public void Shutdown() { _active = false; } } } namespace IronNestStats.Melon.Game { internal sealed class GameLocalizationService { private readonly ReflectionBridge _bridge; public string CurrentLanguage { get { object instance = _bridge.GetStatic("Localisation.LocalisationManager", "Instance"); string text = _bridge.Text(_bridge.Get(instance, "CurrentLanguage")); if (!string.IsNullOrEmpty(text)) { return text; } return "English"; } } public GameLocalizationService(ReflectionBridge bridge) { _bridge = bridge; } public bool IsChinese(string language) { if (!string.IsNullOrEmpty(language)) { return language.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } public string ResolveIdentifier(object identifier, string fallback) { string text = _bridge.Text(_bridge.Invoke(identifier, "Get")); if (!string.IsNullOrEmpty(text)) { return text; } return fallback; } public string GetGameText(string key, string fallback) { if (string.IsNullOrEmpty(key)) { return fallback; } object instance = _bridge.GetStatic("Localisation.LocalisationManager", "Instance"); string text = _bridge.Text(_bridge.Invoke(instance, "Get", key)); if (!string.IsNullOrEmpty(text) && !(text == key)) { return text; } return fallback; } public string GetStatLabel(string statisticId, string language) { StatDefinition val = StatCatalog.Find(statisticId); if (val == null) { return LocalizeFormula(statisticId, language) ?? Humanize(statisticId); } string fallback = (IsChinese(language) ? val.SimplifiedChineseLabel : val.EnglishLabel); return GetGameText(val.GameLocalizationKey, fallback); } public string GetTierLabel(string tier, string language) { bool flag = string.Equals(tier, "Bronze", StringComparison.OrdinalIgnoreCase); bool flag2 = string.Equals(tier, "Silver", StringComparison.OrdinalIgnoreCase); if (Contains(language, "Chinese")) { bool flag3 = Contains(language, "Traditional"); if (!flag) { if (!flag2) { if (!flag3) { return "金牌"; } return "金牌"; } if (!flag3) { return "银牌"; } return "銀牌"; } if (!flag3) { return "铜牌"; } return "銅牌"; } if (Contains(language, "Japanese")) { if (!flag) { if (!flag2) { return "金メダル"; } return "銀メダル"; } return "銅メダル"; } if (Contains(language, "Korean")) { if (!flag) { if (!flag2) { return "금메달"; } return "은메달"; } return "동메달"; } if (Contains(language, "French")) { if (!flag) { if (!flag2) { return "Or"; } return "Argent"; } return "Bronze"; } if (Contains(language, "German")) { if (!flag) { if (!flag2) { return "Gold"; } return "Silber"; } return "Bronze"; } if (Contains(language, "Spanish")) { if (!flag) { if (!flag2) { return "Oro"; } return "Plata"; } return "Bronce"; } if (Contains(language, "Italian")) { if (!flag) { if (!flag2) { return "Oro"; } return "Argento"; } return "Bronzo"; } if (Contains(language, "Portuguese")) { if (!flag) { if (!flag2) { return "Ouro"; } return "Prata"; } return "Bronze"; } if (Contains(language, "Russian")) { if (!flag) { if (!flag2) { return "Золото"; } return "Серебро"; } return "Бронза"; } if (Contains(language, "Polish")) { if (!flag) { if (!flag2) { return "Złoto"; } return "Srebro"; } return "Brąz"; } if (Contains(language, "Turkish")) { if (!flag) { if (!flag2) { return "Altın"; } return "Gümüş"; } return "Bronz"; } if (!flag) { if (!flag2) { return "Gold"; } return "Silver"; } return "Bronze"; } private string LocalizeFormula(string value, string language) { if (string.IsNullOrEmpty(value)) { return null; } for (int i = 1; i < value.Length - 1; i++) { char c = value[i]; if (c == '+' || c == '-' || c == '*' || c == '/') { string statisticId = value.Substring(0, i); string statisticId2 = value.Substring(i + 1); string text = ((c == '/') ? " / " : c.ToString()); return GetStatLabel(statisticId, language) + text + GetStatLabel(statisticId2, language); } } return null; } private static string Humanize(string value) { if (string.IsNullOrEmpty(value)) { return "Value"; } StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < value.Length; i++) { if (i > 0 && char.IsUpper(value[i]) && char.IsLower(value[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(value[i]); } return stringBuilder.ToString(); } private static bool Contains(string value, string expected) { if (!string.IsNullOrEmpty(value)) { return value.IndexOf(expected, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } } internal sealed class IronNestGameFacade { private sealed class ExpressionInfo { public string Id { get; } public bool IsInline { get; } public IDictionary InputValues { get; } public ExpressionInfo(string id, bool isInline, IDictionary inputValues) { Id = id; IsInline = isInline; InputValues = inputValues; } public static ExpressionInfo ForStatistic(string id, double value) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (!string.IsNullOrEmpty(id)) { dictionary[id] = value; } return new ExpressionInfo(id, isInline: false, dictionary); } } private static readonly string[] TrackedStatisticIds = new string[21] { "Kills", "TargetKills", "EnemyKills", "AllyKills", "StarsKilled", "ShotsFired", "ShotsHit", "STARUsed", "AverageImpactDistanceFromNearestTarget", "FirstShotTime", "LastTargetDestroyedTime", "RequisitionPointsSpent", "ReconUsed", "ReconUsedAfterFirstShot", "LongestKillStreak", "MostKillsBySingleImpact", "BestThreeKillWindowSeconds", "CounterBatteryTimeRemaining", "MissionStartTime", "MissionCompleteTime", "MissionEndTime" }; private static readonly IDictionary TrackerMembers = new Dictionary { { "MissionTime", "MissionTime_Mission" }, { "ShotsFired", "ShotsFired_Mission" }, { "TargetKills", "TargetsDestroyed_Mission" }, { "ShotsHit", "HitsOnTargets_Mission" }, { "ShotsMissed", "MissedShots_Mission" }, { "AccuracyPercent", "Accuracy_Mission" }, { "DirectHits", "DirectHits_Mission" }, { "MaxHitStreak", "MaxHitStreak_Mission" }, { "CurrentRequisitionPoints", "RequisitionPoints" } }; private readonly ReflectionBridge _bridge = new ReflectionBridge(); private readonly GameLocalizationService _localization; public GameLocalizationService Localization => _localization; public IronNestGameFacade() { _localization = new GameLocalizationService(_bridge); } public StatsSnapshot Capture(int maxMedals, bool includeConditions, int maxConditionsPerMedal) { //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown string currentLanguage = _localization.CurrentLanguage; object obj = _bridge.GetStatic("MissionManager", "Instance"); if (obj == null || !IsMissionActive(_bridge.Get(obj, "CurrentPhase"))) { return StatsSnapshot.Inactive(currentLanguage); } object obj2 = _bridge.Get(obj, "CurrentMission"); object instance = _bridge.Get(obj, "CurrentMissionState"); object trackedValues = _bridge.Get(instance, "TrackingValues"); object tracker = _bridge.GetStatic("MissionStatsTracker", "Instance"); string text = _localization.ResolveIdentifier(_bridge.Get(obj2, "MissionName"), "Mission"); IDictionary dictionary = ReadStatistics(trackedValues, tracker); IList list = ReadMedals(obj2, trackedValues, maxMedals, includeConditions, maxConditionsPerMedal); return new StatsSnapshot(true, text, currentLanguage, dictionary, list); } private IDictionary ReadStatistics(object trackedValues, object tracker) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); string[] trackedStatisticIds = TrackedStatisticIds; foreach (string text in trackedStatisticIds) { AddNumber(dictionary, text, _bridge.Get(trackedValues, text)); } foreach (KeyValuePair trackerMember in TrackerMembers) { AddNumber(dictionary, trackerMember.Key, _bridge.Get(tracker, trackerMember.Value)); } if (!dictionary.ContainsKey("ShotsFiredAll") && dictionary.ContainsKey("ShotsFired")) { dictionary["ShotsFiredAll"] = dictionary["ShotsFired"]; } AddTimeDerivatives(dictionary); StatsCalculator.AddDerivedValues((IDictionary)dictionary); return dictionary; } private IList ReadMedals(object mission, object trackedValues, int maxMedals, bool includeConditions, int maxConditionsPerMedal) { //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown List list = new List(); if (mission == null || trackedValues == null || maxMedals <= 0) { return list; } IList list2 = _bridge.Enumerate(_bridge.Get(mission, "Medals")); for (int i = 0; i < list2.Count; i++) { if (list.Count >= maxMedals) { break; } object instance = list2[i]; string text = _localization.ResolveIdentifier(_bridge.Get(instance, "displayNameV2"), "Medal " + (i + 1)); object conditionSet = _bridge.Get(instance, "BronzeConditions"); object conditionSet2 = _bridge.Get(instance, "SilverConditions"); object conditionSet3 = _bridge.Get(instance, "GoldConditions"); MedalTierSnapshot val = CreateTierSnapshot("Bronze", conditionSet, trackedValues, includeConditions, maxConditionsPerMedal); MedalTierSnapshot val2 = CreateTierSnapshot("Silver", conditionSet2, trackedValues, includeConditions, maxConditionsPerMedal); MedalTierSnapshot val3 = CreateTierSnapshot("Gold", conditionSet3, trackedValues, includeConditions, maxConditionsPerMedal); List list3 = new List { val, val2, val3 }; double num = ((!val.Achieved) ? (val.Progress / 3.0) : ((!val2.Achieved) ? ((1.0 + val2.Progress) / 3.0) : ((!val3.Achieved) ? ((2.0 + val3.Progress) / 3.0) : 1.0))); list.Add(new MedalSnapshot(text, num, (IList)list3)); } return list; } private MedalTierSnapshot CreateTierSnapshot(string tier, object conditionSet, object trackedValues, bool includeConditions, int maxConditions) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown bool flag = _bridge.Boolean(_bridge.Invoke(conditionSet, "Resolve", trackedValues)); IList list = ReadConditions(conditionSet, trackedValues, int.MaxValue); List list2 = new List(); if (includeConditions) { for (int i = 0; i < list.Count && i < maxConditions; i++) { list2.Add(list[i]); } } return new MedalTierSnapshot(tier, flag, flag ? 1.0 : CalculateMedalProgress(list), (IList)list2); } private IList ReadConditions(object conditionSet, object trackedValues, int limit) { //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Expected O, but got Unknown //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Expected O, but got Unknown List list = new List(); IList list2 = _bridge.Enumerate(_bridge.Get(conditionSet, "Conditions")); for (int i = 0; i < list2.Count; i++) { if (list.Count >= limit) { break; } object obj = list2[i]; object instance = _bridge.Get(obj, "Condition") ?? obj; object obj2 = _bridge.Get(instance, "Left"); object obj3 = _bridge.Get(instance, "Right"); double num = _bridge.Number(_bridge.Invoke(obj2, "Resolve", trackedValues)); double num2 = _bridge.Number(_bridge.Invoke(obj3, "Resolve", trackedValues)); ExpressionInfo expressionInfo = DescribeExpression(obj2, trackedValues); ExpressionInfo expressionInfo2 = DescribeExpression(obj3, trackedValues); string text = CompareText(_bridge.Text(_bridge.Get(instance, "Operator"))); if (expressionInfo.IsInline && !expressionInfo2.IsInline) { list.Add(new MedalConditionSnapshot(expressionInfo2.Id ?? "Value", num2, InvertComparison(text), num, expressionInfo2.InputValues)); } else { list.Add(new MedalConditionSnapshot(expressionInfo.Id ?? "Value", num, text, num2, expressionInfo.InputValues)); } } return list; } private ExpressionInfo DescribeExpression(object expression, object trackedValues) { string text = _bridge.Text(_bridge.Get(expression, "Mode")); if (!string.IsNullOrEmpty(text) && !text.Equals("Value", StringComparison.OrdinalIgnoreCase)) { ExpressionInfo expressionInfo = DescribeOperand(_bridge.Get(expression, "A"), trackedValues); ExpressionInfo expressionInfo2 = DescribeOperand(_bridge.Get(expression, "B"), trackedValues); string text2 = MathOperatorText(_bridge.Text(_bridge.Get(expression, "MathOperator"))); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); MergeInputs(dictionary, expressionInfo.InputValues); MergeInputs(dictionary, expressionInfo2.InputValues); return new ExpressionInfo((expressionInfo.Id ?? "Value") + text2 + (expressionInfo2.Id ?? "Value"), isInline: false, dictionary); } string text3 = _bridge.Text(_bridge.Get(expression, "Source")); if (text3.Equals("Variable", StringComparison.OrdinalIgnoreCase)) { string id = _bridge.Text(_bridge.Get(expression, "Variable")); double value = _bridge.Number(_bridge.Invoke(expression, "Resolve", trackedValues)); return ExpressionInfo.ForStatistic(id, value); } if (text3.Equals("CustomVariable", StringComparison.OrdinalIgnoreCase)) { string id2 = _bridge.Text(_bridge.Get(expression, "CustomVariableKey")); double value2 = _bridge.Number(_bridge.Invoke(expression, "Resolve", trackedValues)); return ExpressionInfo.ForStatistic(id2, value2); } return new ExpressionInfo(null, isInline: true, new Dictionary()); } private ExpressionInfo DescribeOperand(object operand, object trackedValues) { string text = _bridge.Text(_bridge.Get(operand, "Source")); double value = _bridge.Number(_bridge.Invoke(operand, "Resolve", trackedValues)); if (text.Equals("Variable", StringComparison.OrdinalIgnoreCase)) { return ExpressionInfo.ForStatistic(_bridge.Text(_bridge.Get(operand, "Variable")), value); } if (text.Equals("CustomVariable", StringComparison.OrdinalIgnoreCase)) { return ExpressionInfo.ForStatistic(_bridge.Text(_bridge.Get(operand, "CustomVariableKey")), value); } return new ExpressionInfo(value.ToString("0.##", CultureInfo.InvariantCulture), isInline: true, new Dictionary()); } private static void MergeInputs(IDictionary target, IDictionary source) { foreach (KeyValuePair item in source) { target[item.Key] = item.Value; } } private static string MathOperatorText(string value) { return value switch { "Divide" => "/", "Multiply" => "*", "Subtract" => "-", "Add" => "+", _ => "?", }; } private int ResolveTier(object bronze, object silver, object gold, object trackedValues) { if (_bridge.Boolean(_bridge.Invoke(gold, "Resolve", trackedValues))) { return 3; } if (_bridge.Boolean(_bridge.Invoke(silver, "Resolve", trackedValues))) { return 2; } if (_bridge.Boolean(_bridge.Invoke(bronze, "Resolve", trackedValues))) { return 1; } return 0; } private static double CalculateMedalProgress(IList conditions) { if (conditions == null || conditions.Count == 0) { return 0.0; } double num = 1.0; for (int i = 0; i < conditions.Count; i++) { num = Math.Min(num, CalculateConditionProgress(conditions[i])); } return Math.Max(0.0, Math.Min(1.0, num)); } private static double CalculateConditionProgress(MedalConditionSnapshot condition) { double currentValue = condition.CurrentValue; double targetValue = condition.TargetValue; switch (condition.Comparison) { case ">": case ">=": if (currentValue >= targetValue) { return 1.0; } if (!(targetValue <= 0.0)) { return currentValue / targetValue; } return 0.0; case "<": case "<=": if (currentValue <= targetValue) { return 1.0; } if (!(currentValue <= 0.0)) { return targetValue / currentValue; } return 0.0; case "=": { if (Math.Abs(currentValue - targetValue) < 0.0001) { return 1.0; } double num = Math.Max(Math.Abs(currentValue), Math.Abs(targetValue)); if (!(num <= 0.0)) { return Math.Min(Math.Abs(currentValue), Math.Abs(targetValue)) / num; } return 0.0; } case "!=": return (Math.Abs(currentValue - targetValue) >= 0.0001) ? 1 : 0; default: return 0.0; } } private static bool IsMissionActive(object phase) { if (phase == null) { return false; } string text = phase.ToString(); if (string.Equals(text, "MissionActive", StringComparison.OrdinalIgnoreCase)) { return true; } if (int.TryParse(text, out var result)) { return result == 2; } return false; } private void AddNumber(IDictionary target, string id, object value) { if (value != null) { target[id] = _bridge.Number(value); } } private static void AddTimeDerivatives(IDictionary values) { double num = StatsCalculator.Get(values, "MissionStartTime"); double num2 = StatsCalculator.Get(values, "FirstShotTime"); double num3 = StatsCalculator.Get(values, "LastTargetDestroyedTime"); if (num2 > 0.0 && num > 0.0) { values["TimeToFirstShot"] = Math.Max(0.0, num2 - num); } if (num3 > 0.0 && num > 0.0) { values["TimeToLastTargetKill"] = Math.Max(0.0, num3 - num); } if (num3 > 0.0 && num2 > 0.0) { values["TimeFromFirstShotToLastTargetKill"] = Math.Max(0.0, num3 - num2); } } private static string TierName(int tier) { return tier switch { 1 => "Bronze", 2 => "Silver", 3 => "Gold", _ => "Unearned", }; } private static string CompareText(string value) { return value switch { "Equal" => "=", "NotEqual" => "!=", "GreaterThan" => ">", "GreaterThanOrEqual" => ">=", "LessThan" => "<", "LessThanOrEqual" => "<=", _ => value ?? "?", }; } private static string InvertComparison(string value) { return value switch { "<=" => ">=", "<" => ">", ">=" => "<=", ">" => "<", _ => value, }; } } internal sealed class ReflectionBridge { private readonly Dictionary _types = new Dictionary(StringComparer.Ordinal); private readonly Dictionary _members = new Dictionary(StringComparer.Ordinal); public Type FindType(string fullName) { if (_types.TryGetValue(fullName, out var value)) { return value; } List list = new List { fullName }; if (fullName.LastIndexOf('.') < 0) { list.Add("Il2Cpp." + fullName); } else { list.Add("Il2Cpp" + fullName); } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { foreach (string item in list) { Type type = assembly.GetType(item, throwOnError: false); if (!(type == null)) { _types[fullName] = type; return type; } } } return null; } public object GetStatic(string typeName, string memberName) { Type type = FindType(typeName); if (!(type == null)) { return GetValue(type, null, memberName, BindingFlags.Static); } return null; } public object Get(object instance, string memberName) { if (instance != null) { return GetValue(instance.GetType(), instance, memberName, BindingFlags.Instance); } return null; } public object Invoke(object instance, string methodName, params object[] arguments) { if (instance == null) { return null; } foreach (MethodInfo item in from method in instance.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where method.Name == methodName && method.GetParameters().Length == arguments.Length select method) { try { return item.Invoke(instance, AdaptArguments(item.GetParameters(), arguments)); } catch (ArgumentException) { } } return null; } public IList Enumerate(object collection) { List list = new List(); if (collection == null) { return list; } if (collection is IEnumerable enumerable) { { foreach (object item in enumerable) { if (item != null) { list.Add(item); } } return list; } } object value = Get(collection, "Count") ?? Get(collection, "Length"); int num = ToInt(value); for (int i = 0; i < num; i++) { object obj = Invoke(collection, "get_Item", i); if (obj != null) { list.Add(obj); } } return list; } public string Text(object value) { if (value != null) { return value.ToString(); } return string.Empty; } public double Number(object value, double fallback = 0.0) { if (value == null) { return fallback; } try { return Convert.ToDouble(value); } catch { return fallback; } } public bool Boolean(object value, bool fallback = false) { if (value == null) { return fallback; } try { return Convert.ToBoolean(value); } catch { return fallback; } } public int ToInt(object value, int fallback = 0) { if (value == null) { return fallback; } try { return Convert.ToInt32(value); } catch { return fallback; } } private object GetValue(Type type, object instance, string memberName, BindingFlags scope) { BindingFlags bindingAttr = BindingFlags.Public | BindingFlags.NonPublic | scope; string key = type.AssemblyQualifiedName + "|" + scope.ToString() + "|" + memberName; if (!_members.TryGetValue(key, out var value)) { value = (MemberInfo)(((object)type.GetProperty(memberName, bindingAttr)) ?? ((object)type.GetField(memberName, bindingAttr))); _members[key] = value; } PropertyInfo propertyInfo = value as PropertyInfo; if (propertyInfo != null) { return propertyInfo.GetValue(instance, null); } FieldInfo fieldInfo = value as FieldInfo; if (!(fieldInfo == null)) { return fieldInfo.GetValue(instance); } return null; } private static object[] AdaptArguments(ParameterInfo[] parameters, object[] arguments) { object[] array = new object[arguments.Length]; for (int i = 0; i < arguments.Length; i++) { object obj = arguments[i]; Type parameterType = parameters[i].ParameterType; if (obj is string && parameterType.FullName == "Il2CppSystem.String") { MethodInfo methodInfo = parameterType.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo method) => method.Name == "op_Implicit" && method.GetParameters().Length == 1 && method.GetParameters()[0].ParameterType == typeof(string)); array[i] = ((methodInfo == null) ? obj : methodInfo.Invoke(null, new object[1] { obj })); } else { array[i] = obj; } } return array; } } } namespace IronNestStats.Melon.Configuration { public sealed class HotkeyBinding { private readonly Key _mainKey; private readonly IList _modifiers; private HotkeyBinding(Key mainKey, IList modifiers) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) _mainKey = mainKey; _modifiers = modifiers; } public bool IsDown() { //IL_0007: 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_0032: Unknown result type (might be due to invalid IL or missing references) Keyboard current = Keyboard.current; if ((int)_mainKey == 0 || current == null || !((ButtonControl)current[_mainKey]).wasPressedThisFrame) { return false; } for (int i = 0; i < _modifiers.Count; i++) { if (!((ButtonControl)current[_modifiers[i]]).isPressed) { return false; } } return true; } public static HotkeyBinding Parse(string configured, string fallback) { //IL_0014: 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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Invalid comparison between Unknown and I4 //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Invalid comparison between Unknown and I4 //IL_0076: 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_00ab: Invalid comparison between Unknown and I4 //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Invalid comparison between Unknown and I4 //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Invalid comparison between Unknown and I4 //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Invalid comparison between Unknown and I4 //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) string text = (string.IsNullOrWhiteSpace(configured) ? fallback : configured); List list = new List(); Key val = (Key)0; string[] array = text.Split(new char[4] { '+', ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); Key result; if (text2.Equals("Ctrl", StringComparison.OrdinalIgnoreCase) || text2.Equals("Control", StringComparison.OrdinalIgnoreCase)) { result = (Key)55; } else if (text2.Equals("Alt", StringComparison.OrdinalIgnoreCase)) { result = (Key)53; } else if (text2.Equals("Shift", StringComparison.OrdinalIgnoreCase)) { result = (Key)51; } else if (!Enum.TryParse(text2, ignoreCase: true, out result)) { continue; } if ((int)result == 55 || (int)result == 56 || (int)result == 53 || (int)result == 54 || (int)result == 51 || (int)result == 52) { list.Add(result); } else { val = result; } } if ((int)val != 0 || string.Equals(text, fallback, StringComparison.OrdinalIgnoreCase)) { return new HotkeyBinding(val, list); } return Parse(fallback, fallback); } } public sealed class ModSettings { public MelonPreferences_Entry ShowMedalsByDefault { get; } public MelonPreferences_Entry ShowStatsByDefault { get; } public MelonPreferences_Entry ToggleMedals { get; } public MelonPreferences_Entry ToggleStats { get; } public MelonPreferences_Entry MaxMedals { get; } public MelonPreferences_Entry ShowMedalConditions { get; } public MelonPreferences_Entry MaxConditionsPerMedal { get; } public MelonPreferences_Entry MedalPanelWidth { get; } public MelonPreferences_Entry StatsPanelWidth { get; } public MelonPreferences_Entry PanelHeight { get; } public MelonPreferences_Entry BackgroundOpacity { get; } public MelonPreferences_Entry RefreshInterval { get; } public HotkeyBinding ToggleMedalsBinding => HotkeyBinding.Parse(ToggleMedals.Value, "F7"); public HotkeyBinding ToggleStatsBinding => HotkeyBinding.Parse(ToggleStats.Value, "F8"); public int MedalLimit => Math.Max(0, Math.Min(8, MaxMedals.Value)); public int ConditionLimit => Math.Max(0, Math.Min(8, MaxConditionsPerMedal.Value)); public float RefreshSeconds => Math.Max(0.1f, Math.Min(2f, RefreshInterval.Value)); public float Opacity => Math.Max(0.05f, Math.Min(1f, BackgroundOpacity.Value)); public ModSettings() { MelonPreferences_Category val = MelonPreferences.CreateCategory("MissionStats", "任务统计 / Mission Stats"); val.SetFilePath(Path.Combine(MelonEnvironment.UserDataDirectory, "MissionStats.cfg"), true, false); ShowMedalsByDefault = val.CreateEntry("ShowMedalsByDefault", true, "默认显示奖章 / Show medals by default", "进入任务时显示奖章面板。 / Show the medal panel when a mission starts.", false, false, (ValueValidator)null, (string)null); ShowStatsByDefault = val.CreateEntry("ShowStatsByDefault", false, "默认显示详细统计 / Show statistics by default", "进入任务时显示详细统计面板。 / Show the detailed statistics panel when a mission starts.", false, false, (ValueValidator)null, (string)null); ToggleMedals = val.CreateEntry("ToggleMedals", "F7", "奖章面板快捷键 / Medal panel hotkey", "显示或隐藏奖章面板。 / Show or hide the medal panel.", false, false, (ValueValidator)null, (string)null); ToggleStats = val.CreateEntry("ToggleStats", "F8", "详细统计快捷键 / Statistics panel hotkey", "显示或隐藏详细统计面板。 / Show or hide the detailed statistics panel.", false, false, (ValueValidator)null, (string)null); MaxMedals = val.CreateEntry("MaxCurrentMedals", 4, "最大奖章数量 / Maximum medals", "最多显示的当前关卡奖章数量。 / Maximum number of current-mission medals shown.", false, false, (ValueValidator)null, (string)null); ShowMedalConditions = val.CreateEntry("ShowConditions", true, "显示奖章条件 / Show medal conditions", "显示奖章条件及相关统计。 / Show medal conditions and their related statistics.", false, false, (ValueValidator)null, (string)null); MaxConditionsPerMedal = val.CreateEntry("MaxConditionsPerMedal", 3, "每级最多条件数 / Maximum conditions per tier", "每个奖章等级最多读取的条件数。 / Maximum conditions read for each medal tier.", false, false, (ValueValidator)null, (string)null); MedalPanelWidth = val.CreateEntry("MedalPanelWidth", 520f, "奖章面板最大宽度 / Maximum medal panel width", "奖章面板的最大像素宽度,实际宽度根据内容调整。 / Maximum medal panel width in pixels; actual width follows its content.", false, false, (ValueValidator)null, (string)null); StatsPanelWidth = val.CreateEntry("StatsPanelWidth", 980f, "详细统计面板宽度 / Statistics panel width", "详细统计面板的像素宽度。 / Detailed statistics panel width in pixels.", false, false, (ValueValidator)null, (string)null); PanelHeight = val.CreateEntry("PanelHeight", 650f, "面板最大高度 / Maximum panel height", "奖章和详细统计面板的最大像素高度。 / Maximum height of the medal and statistics panels in pixels.", false, false, (ValueValidator)null, (string)null); BackgroundOpacity = val.CreateEntry("BackgroundOpacity", 0.78f, "背景不透明度 / Background opacity", "面板背景不透明度,范围为 0.0 到 1.0。 / Panel background opacity from 0.0 to 1.0.", false, false, (ValueValidator)null, (string)null); RefreshInterval = val.CreateEntry("RefreshInterval", 0.2f, "刷新间隔 / Refresh interval", "统计数据刷新间隔,单位为秒。 / Statistics refresh interval in seconds.", false, false, (ValueValidator)null, (string)null); val.SaveToFile(false); } } }