Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ComfortMe v1.3.1
ComfortMe.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("ComfortMe")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Hammer helper that marks comfort pieces which would raise the room you are standing in.")] [assembly: AssemblyFileVersion("1.3.1.0")] [assembly: AssemblyInformationalVersion("1.3.1+dc2c013a7936dbe0b0d6920547a60dad7684f06a")] [assembly: AssemblyProduct("ComfortMe")] [assembly: AssemblyTitle("ComfortMe")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.1.0")] [module: UnverifiableCode] [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 ComfortMe { [BepInPlugin("hardwire99.comfortme", "ComfortMe", "1.3.1")] public sealed class ComfortMePlugin : BaseUnityPlugin { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__2_0; internal void <Awake>b__2_0(ConsoleEventArgs _) { GroupCatalogPanel.Dump(); } } internal static ManualLogSource Log; private Harmony harmony; private void Awake() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_00f5: 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_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); if (!ModConfig.Enabled.Value) { Log.LogInfo((object)"ComfortMe 1.3.1 is disabled in config."); return; } harmony = new Harmony("hardwire99.comfortme"); try { harmony.PatchAll(typeof(ComfortMePlugin).Assembly); Log.LogInfo((object)$"Harmony applied {harmony.GetPatchedMethods().Count()} patch(es)."); } catch (Exception arg) { Log.LogError((object)$"Harmony PatchAll failed: {arg}"); } ModLocalization.Hook(); Log.LogInfo((object)("ComfortMe 1.3.1 loaded (1.3.1, dll " + GetDllTimestamp() + ").")); object obj = <>c.<>9__2_0; if (obj == null) { ConsoleEvent val = delegate { GroupCatalogPanel.Dump(); }; <>c.<>9__2_0 = val; obj = (object)val; } new ConsoleCommand("comfortme", "Dump ComfortMe catalog HUD diagnostics", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private static string GetDllTimestamp() { string location = Assembly.GetExecutingAssembly().Location; if (!File.Exists(location)) { return "unknown"; } return File.GetLastWriteTime(location).ToString("yyyy-MM-dd HH:mm:ss"); } } internal enum ComfortVerdict { Skip, NewGroup, Upgrade, Redundant, Unlit, Downgrade } internal readonly struct ComfortClassification { internal ComfortVerdict Verdict { get; } internal ComfortGroup Group { get; } internal int Current { get; } internal int Candidate { get; } internal int Delta { get; } internal bool IsUpgrade { get { if (Verdict != ComfortVerdict.Upgrade) { return Verdict == ComfortVerdict.NewGroup; } return true; } } internal ComfortClassification(ComfortVerdict verdict, ComfortGroup group, int current, int candidate) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Verdict = verdict; Group = group; Current = current; Candidate = candidate; Delta = candidate - current; } internal static int ShopRank(ComfortVerdict verdict) { switch (verdict) { case ComfortVerdict.NewGroup: case ComfortVerdict.Upgrade: return 0; case ComfortVerdict.Unlit: return 1; case ComfortVerdict.Redundant: return 2; case ComfortVerdict.Downgrade: return 3; default: return 4; } } } internal sealed class ComfortSnapshot { private const float CacheSeconds = 0.2f; private const float CacheMoveSqr = 0.0625f; private static ComfortSnapshot cached; private static Vector3 cachedPos; private static float cachedTime = -999f; private static bool cachedShelter; private readonly HashSet<string> placedKeys = new HashSet<string>(); private readonly HashSet<string> activeKeys = new HashSet<string>(); private readonly HashSet<string> inactiveKeys = new HashSet<string>(); internal static ComfortSnapshot Empty { get; } = new ComfortSnapshot(); internal int Total { get; private set; } internal bool InShelter { get; private set; } internal Dictionary<ComfortGroup, int> GroupMax { get; } = new Dictionary<ComfortGroup, int>(); internal Dictionary<ComfortGroup, string> GroupSource { get; } = new Dictionary<ComfortGroup, string>(); internal List<UniqueComfort> Uniques { get; } = new List<UniqueComfort>(); internal List<Piece> CountingPieces { get; } = new List<Piece>(); internal static ComfortSnapshot Capture(Player player) { //IL_0015: 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_0086: 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_0043: 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0182: 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) if ((Object)(object)player == (Object)null) { return Empty; } Vector3 position = ((Component)player).transform.position; bool flag = player.InShelter(); float time = Time.time; if (cached != null && time - cachedTime < 0.2f) { Vector3 val = position - cachedPos; if (((Vector3)(ref val)).sqrMagnitude < 0.0625f && flag == cachedShelter) { return cached; } } ComfortSnapshot comfortSnapshot = new ComfortSnapshot { InShelter = flag, Total = SE_Rested.CalculateComfortLevel(player) }; List<Piece> list = new List<Piece>(); Piece.GetAllComfortPiecesInRadius(position, 10f, list); Dictionary<ComfortGroup, Piece> dictionary = new Dictionary<ComfortGroup, Piece>(); for (int i = 0; i < list.Count; i++) { Piece val2 = list[i]; if ((Object)(object)val2 == (Object)null || val2.m_comfort <= 0) { continue; } AddIdentity(comfortSnapshot.placedKeys, val2); int comfort = val2.GetComfort(); if (comfort > 0) { AddIdentity(comfortSnapshot.activeKeys, val2); ComfortGroup comfortGroup = val2.m_comfortGroup; string local = PieceDiscovery.LocalName(val2); int value; if ((int)comfortGroup == 0) { if (!comfortSnapshot.Uniques.Exists((UniqueComfort u) => u.Name == local)) { comfortSnapshot.Uniques.Add(new UniqueComfort(local, comfort)); comfortSnapshot.CountingPieces.Add(val2); } } else if (!comfortSnapshot.GroupMax.TryGetValue(comfortGroup, out value) || comfort > value) { comfortSnapshot.GroupMax[comfortGroup] = comfort; comfortSnapshot.GroupSource[comfortGroup] = local; dictionary[comfortGroup] = val2; } } else { AddIdentity(comfortSnapshot.inactiveKeys, val2); } } foreach (KeyValuePair<ComfortGroup, Piece> item in dictionary) { comfortSnapshot.CountingPieces.Add(item.Value); } cached = comfortSnapshot; cachedPos = position; cachedTime = time; cachedShelter = flag; return comfortSnapshot; } internal int CurrentFor(ComfortGroup group) { //IL_0000: 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) if ((int)group == 0) { return 0; } if (!GroupMax.TryGetValue(group, out var value)) { return 0; } return value; } internal bool HasPlaced(Piece piece) { return Matches(placedKeys, piece); } internal bool HasActive(Piece piece) { return Matches(activeKeys, piece); } internal bool IsInactivePresent(Piece piece) { if (Matches(inactiveKeys, piece)) { return !Matches(activeKeys, piece); } return false; } private static void AddIdentity(HashSet<string> keys, Piece piece) { if (!string.IsNullOrEmpty(piece.m_name)) { keys.Add(piece.m_name); } string text = PrefabName(piece); if (!string.IsNullOrEmpty(text)) { keys.Add(text); } } private static bool Matches(HashSet<string> keys, Piece piece) { if ((Object)(object)piece == (Object)null || keys.Count == 0) { return false; } if (!string.IsNullOrEmpty(piece.m_name) && keys.Contains(piece.m_name)) { return true; } string text = PrefabName(piece); if (!string.IsNullOrEmpty(text)) { return keys.Contains(text); } return false; } private static string PrefabName(Piece piece) { if (!((Object)(object)((Component)piece).gameObject != (Object)null)) { return string.Empty; } return Utils.GetPrefabName(((Component)piece).gameObject); } } internal readonly struct UniqueComfort { internal string Name { get; } internal int Comfort { get; } internal UniqueComfort(string name, int comfort) { Name = name; Comfort = comfort; } } internal readonly struct DiscoveredPiece { internal Piece Piece { get; } internal PieceCategory Category { get; } internal DiscoveredPiece(Piece piece, PieceCategory category) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) Piece = piece; Category = category; } } internal static class PieceDiscovery { private static readonly FieldInfo BuildPiecesField = AccessTools.Field(typeof(Player), "m_buildPieces"); private static readonly FieldInfo AvailableByCategoryField = AccessTools.Field(typeof(PieceTable), "m_availablePiecesByCategory"); private static readonly UsageTagFlags[] UsageFlags = (UsageTagFlags[])Enum.GetValues(typeof(UsageTagFlags)); internal static PieceTable GetTable(Player player) { if (!((Object)(object)player == (Object)null)) { object? obj = BuildPiecesField?.GetValue(player); return (PieceTable)((obj is PieceTable) ? obj : null); } return null; } internal static List<DiscoveredPiece> AllComfort(Player player) { List<DiscoveredPiece> list = new List<DiscoveredPiece>(); PieceTable table = GetTable(player); if ((Object)(object)table == (Object)null) { return list; } HashSet<Piece> seen = new HashSet<Piece>(); if (table.m_availablePieces != null) { foreach (Piece availablePiece in table.m_availablePieces) { AddComfort(list, seen, availablePiece); } } if (list.Count == 0) { AddFromCategoryLists(list, seen, table); } return list; } internal static int BestUpgradeComfort(Player player, ComfortGroup group, ComfortSnapshot snapshot) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) int num = 0; List<DiscoveredPiece> list = AllComfort(player); for (int i = 0; i < list.Count; i++) { Piece piece = list[i].Piece; if (piece.m_comfortGroup == group && UpgradeClassifier.Classify(piece, snapshot).IsUpgrade && piece.m_comfort > num) { num = piece.m_comfort; } } return num; } internal static string CategoryLabel(PieceTable table, PieceCategory category) { //IL_006a: 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) if ((Object)(object)table != (Object)null && table.m_categories != null && table.m_categoryLabels != null) { int num = table.m_categories.IndexOf(category); if (num >= 0 && num < table.m_categoryLabels.Count) { string text = table.m_categoryLabels[num]; if (!string.IsNullOrEmpty(text) && Localization.instance != null) { return Localization.instance.Localize(text); } if (!string.IsNullOrEmpty(text)) { return text; } } } return CategoryFallback(category); } internal static string AppearanceSummary(Piece piece, PieceTable table) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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_003f: 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_0044: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece == (Object)null) { return string.Empty; } List<string> list = new List<string>(); AddUnique(list, CategoryLabel(table, piece.m_category)); UsageTagFlags usage = piece.m_usage; for (int i = 0; i < UsageFlags.Length; i++) { UsageTagFlags val = UsageFlags[i]; if ((int)val != 0 && (UsageTagFlags)(usage & val) == val) { AddUnique(list, UsageLabel(val)); } } return string.Join(", ", list); } internal static string LocalName(Piece piece) { if ((Object)(object)piece == (Object)null || string.IsNullOrEmpty(piece.m_name)) { return string.Empty; } if (Localization.instance == null) { return piece.m_name; } return Localization.instance.Localize(piece.m_name); } internal unsafe static string GroupLabel(ComfortGroup group) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected I4, but got Unknown string key; switch ((int)group) { case 1: key = "comfortme_group_fire"; break; case 2: key = "comfortme_group_bed"; break; case 3: key = "comfortme_group_banner"; break; case 4: key = "comfortme_group_chair"; break; case 5: key = "comfortme_group_table"; break; case 6: key = "comfortme_group_carpet"; break; case 7: key = "comfortme_group_display"; break; case 8: key = "comfortme_group_decor"; break; case 9: key = "comfortme_group_garland"; break; case 10: key = "comfortme_group_lantern"; break; case 11: key = "comfortme_group_leisure"; break; case 0: key = "comfortme_group_none"; break; default: return ((object)(*(ComfortGroup*)(&group))/*cast due to .constrained prefix*/).ToString(); } return ModLocalization.T(key); } private static void AddFromCategoryLists(List<DiscoveredPiece> result, HashSet<Piece> seen, PieceTable table) { if (!(AvailableByCategoryField?.GetValue(table) is IList list)) { return; } for (int i = 0; i < list.Count; i++) { if (list[i] is IList list2) { for (int j = 0; j < list2.Count; j++) { object? obj = list2[j]; AddComfort(result, seen, (Piece)((obj is Piece) ? obj : null)); } } } } private static void AddComfort(List<DiscoveredPiece> result, HashSet<Piece> seen, Piece piece) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)piece == (Object)null) && piece.m_comfort > 0 && seen.Add(piece)) { result.Add(new DiscoveredPiece(piece, piece.m_category)); } } private static void AddUnique(List<string> parts, string label) { if (string.IsNullOrEmpty(label)) { return; } for (int i = 0; i < parts.Count; i++) { if (string.Equals(parts[i], label, StringComparison.OrdinalIgnoreCase)) { return; } } parts.Add(label); } private unsafe static string UsageLabel(UsageTagFlags flag) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) string text = Utils.GetAttributeOfType<DisplayNameAttribute>((Enum)(object)flag)?.DisplayName; if (!string.IsNullOrEmpty(text) && Localization.instance != null) { return Localization.instance.Localize(text); } if (!string.IsNullOrEmpty(text)) { return text; } return ((object)(*(UsageTagFlags*)(&flag))/*cast due to .constrained prefix*/).ToString(); } private unsafe static string CategoryFallback(PieceCategory category) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected I4, but got Unknown return (int)category switch { 0 => "Misc", 1 => "Crafting", 2 => "Building", 3 => "Stonecutter", 4 => "Furniture", 5 => "Deep North", 6 => "Feasts", 7 => "Food", 8 => "Meads", _ => ((object)(*(PieceCategory*)(&category))/*cast due to .constrained prefix*/).ToString(), }; } } internal static class UpgradeClassifier { internal static ComfortClassification Classify(Piece piece, ComfortSnapshot snapshot) { //IL_0020: 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_002d: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece == (Object)null || snapshot == null || piece.m_comfort <= 0) { return new ComfortClassification(ComfortVerdict.Skip, (ComfortGroup)0, 0, 0); } ComfortGroup comfortGroup = piece.m_comfortGroup; int comfort = piece.m_comfort; if ((int)comfortGroup == 0) { if (snapshot.HasActive(piece)) { return new ComfortClassification(ComfortVerdict.Redundant, comfortGroup, comfort, comfort); } if (snapshot.HasPlaced(piece)) { return new ComfortClassification(ComfortVerdict.Unlit, comfortGroup, 0, comfort); } return new ComfortClassification(ComfortVerdict.NewGroup, comfortGroup, 0, comfort); } int num = snapshot.CurrentFor(comfortGroup); if (comfort > num) { if (snapshot.IsInactivePresent(piece)) { return new ComfortClassification(ComfortVerdict.Unlit, comfortGroup, num, comfort); } return new ComfortClassification((num == 0) ? ComfortVerdict.NewGroup : ComfortVerdict.Upgrade, comfortGroup, num, comfort); } if (comfort == num) { return new ComfortClassification(ComfortVerdict.Redundant, comfortGroup, num, comfort); } return new ComfortClassification(ComfortVerdict.Downgrade, comfortGroup, num, comfort); } internal static bool ShouldBadge(Player player, Piece piece, ComfortClassification classification, ComfortSnapshot snapshot) { //IL_004d: 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) if (!classification.IsUpgrade) { return false; } if (ModConfig.RequireMaterials.Value && (Object)(object)player != (Object)null && !player.HaveRequirements(piece, (RequirementMode)0)) { return false; } HighlightPolicy value = ModConfig.Policy.Value; if (value == HighlightPolicy.UpgradeOnly && classification.Verdict == ComfortVerdict.NewGroup) { return false; } if (value == HighlightPolicy.BestAvailable && (int)classification.Group != 0 && (Object)(object)player != (Object)null) { int num = PieceDiscovery.BestUpgradeComfort(player, classification.Group, snapshot); if (num > 0) { return piece.m_comfort >= num; } return false; } return true; } } internal static class BuildHudBadge { private const string BadgeName = "ComfortMeBadge"; private static readonly Color ChipBackground = new Color(0.05f, 0.07f, 0.06f, 0.88f); private static readonly Color UpgradeGreen = new Color(0.35f, 0.85f, 0.55f, 1f); private static readonly Color EqualGrey = new Color(0.72f, 0.72f, 0.72f, 1f); private static readonly Color LowerRed = new Color(0.85f, 0.36f, 0.36f, 1f); private static readonly Color UnlitAmber = new Color(0.95f, 0.72f, 0.28f, 1f); private static readonly FieldInfo PieceIconsField = AccessTools.Field(typeof(Hud), "m_pieceIcons"); private static readonly FieldInfo IconGoField = AccessTools.Field(AccessTools.Inner(typeof(Hud), "PieceIconData"), "m_go"); private static readonly FieldInfo BuildUiButtonsField = AccessTools.Field(typeof(BuildUi), "m_pieceButtons"); private static readonly FieldInfo BuildUiSpecialField = AccessTools.Field(typeof(BuildUi), "m_specialPieceButton"); private static readonly FieldInfo PieceButtonIconField = AccessTools.Field(typeof(BuildUiPieceButton), "m_icon"); private static bool loggedBadgeSweep; internal static void Clear() { } internal static void Refresh(Hud hud, ComfortSnapshot snapshot) { if ((Object)(object)hud == (Object)null || snapshot == null || !ModConfig.ShowValueBadge.Value || !HudUi.BuildMenuOpen(hud)) { HideAll(hud); return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { HideAll(hud); return; } TMP_FontAsset font = HudUi.FontFrom(hud); Material fontMaterial = HudUi.FontMaterialFrom(hud); RefreshLegacyGrid(hud, snapshot, localPlayer, font, fontMaterial); RefreshBuildUi(hud, snapshot, localPlayer, font, fontMaterial); } internal static void OnPieceButtonsRebuilt(Hud hud, ComfortSnapshot snapshot) { if ((Object)(object)hud != (Object)null && (Object)(object)hud.m_buildUi != (Object)null) { CleanupStrayBadges(hud.m_buildUi); } Refresh(hud, snapshot); } internal static void ApplyToButton(BuildUiPieceButton button, ComfortSnapshot snapshot) { if (!((Object)(object)button == (Object)null)) { Hud instance = Hud.instance; Player localPlayer = Player.m_localPlayer; if ((Object)(object)instance == (Object)null || snapshot == null || (Object)(object)localPlayer == (Object)null || !ModConfig.ShowValueBadge.Value) { HideOn(BadgeHost(button)); HideOn(((Component)button).gameObject); } else { Apply(BadgeHost(button), button.Piece, snapshot, localPlayer, HudUi.FontFrom(instance), HudUi.FontMaterialFrom(instance)); } } } internal static void ApplyToImage(Image image, Piece piece, ComfortSnapshot snapshot) { Hud instance = Hud.instance; Player localPlayer = Player.m_localPlayer; ApplyToImage(image, piece, snapshot, localPlayer, HudUi.FontFrom(instance), HudUi.FontMaterialFrom(instance)); } private static void RefreshBuildUi(Hud hud, ComfortSnapshot snapshot, Player player, TMP_FontAsset font, Material fontMaterial) { if ((Object)(object)hud.m_buildUi == (Object)null) { return; } if (BuildUiButtonsField?.GetValue(hud.m_buildUi) is IList list) { int num = 0; for (int i = 0; i < list.Count; i++) { object? obj = list[i]; BuildUiPieceButton val = (BuildUiPieceButton)((obj is BuildUiPieceButton) ? obj : null); if (val != null && (Object)(object)val != (Object)null) { if ((Object)(object)val.Piece != (Object)null && val.Piece.m_comfort > 0) { num++; } Apply(BadgeHost(val), val.Piece, snapshot, player, font, fontMaterial); } } if (!loggedBadgeSweep) { loggedBadgeSweep = true; ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)$"BuildUi chips: {list.Count} icons, {num} comfort pieces."); } } } object? obj2 = BuildUiSpecialField?.GetValue(hud.m_buildUi); BuildUiPieceButton val2 = (BuildUiPieceButton)((obj2 is BuildUiPieceButton) ? obj2 : null); if (val2 != null && (Object)(object)val2 != (Object)null) { Apply(BadgeHost(val2), val2.Piece, snapshot, player, font, fontMaterial); } } private static void RefreshLegacyGrid(Hud hud, ComfortSnapshot snapshot, Player player, TMP_FontAsset font, Material fontMaterial) { if ((Object)(object)hud.m_pieceListRoot != (Object)null && !((Component)hud.m_pieceListRoot).gameObject.activeInHierarchy) { return; } List<Piece> buildPieces = player.GetBuildPieces(); List<GameObject> iconObjects = GetIconObjects(hud); if (buildPieces == null || iconObjects.Count == 0) { return; } int num = Mathf.Min(buildPieces.Count, iconObjects.Count); for (int i = 0; i < iconObjects.Count; i++) { GameObject val = iconObjects[i]; if (!((Object)(object)val == (Object)null)) { if (i >= num) { HideOn(val); } else { Apply(val, buildPieces[i], snapshot, player, font, fontMaterial); } } } } private static void ApplyToImage(Image image, Piece piece, ComfortSnapshot snapshot, Player player, TMP_FontAsset font, Material fontMaterial) { if (!((Object)(object)image == (Object)null)) { Apply(((Component)image).gameObject, piece, snapshot, player, font, fontMaterial); } } private static void Apply(GameObject iconGo, Piece piece, ComfortSnapshot snapshot, Player player, TMP_FontAsset font, Material fontMaterial) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_004c: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)iconGo == (Object)null) { return; } ComfortClassification classification = UpgradeClassifier.Classify(piece, snapshot); bool flag; Color color; if (!ModConfig.ShowValueBadge.Value || classification.Verdict == ComfortVerdict.Skip) { flag = false; color = UpgradeGreen; } else if (ModConfig.BadgeUpgradesOnly.Value) { flag = UpgradeClassifier.ShouldBadge(player, piece, classification, snapshot); color = UpgradeGreen; } else { flag = true; if (ModConfig.RequireMaterials.Value && (Object)(object)player != (Object)null && !player.HaveRequirements(piece, (RequirementMode)0)) { flag = false; } if (ModConfig.Policy.Value == HighlightPolicy.UpgradeOnly && classification.Verdict == ComfortVerdict.NewGroup) { flag = false; } color = ColorFor(classification.Verdict); } string label = ((flag && (Object)(object)piece != (Object)null) ? ("+" + piece.m_comfort) : string.Empty); SetBadge(iconGo, font, fontMaterial, flag, label, color); } private static Color ColorFor(ComfortVerdict verdict) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) switch (verdict) { case ComfortVerdict.NewGroup: case ComfortVerdict.Upgrade: return UpgradeGreen; case ComfortVerdict.Downgrade: return LowerRed; case ComfortVerdict.Unlit: return UnlitAmber; default: return EqualGrey; } } private static void HideAll(Hud hud) { if ((Object)(object)hud == (Object)null) { return; } List<GameObject> iconObjects = GetIconObjects(hud); for (int i = 0; i < iconObjects.Count; i++) { HideOn(iconObjects[i]); } if ((Object)(object)hud.m_buildUi != (Object)null && BuildUiButtonsField?.GetValue(hud.m_buildUi) is IList list) { for (int j = 0; j < list.Count; j++) { object? obj = list[j]; BuildUiPieceButton val = (BuildUiPieceButton)((obj is BuildUiPieceButton) ? obj : null); if (val != null && (Object)(object)val != (Object)null) { HideOn(BadgeHost(val)); HideOn(((Component)val).gameObject); } } } if ((Object)(object)hud.m_buildUi != (Object)null) { object? obj2 = BuildUiSpecialField?.GetValue(hud.m_buildUi); BuildUiPieceButton val2 = (BuildUiPieceButton)((obj2 is BuildUiPieceButton) ? obj2 : null); if (val2 != null && (Object)(object)val2 != (Object)null) { HideOn(BadgeHost(val2)); HideOn(((Component)val2).gameObject); } } if ((Object)(object)hud.m_buildIcon != (Object)null) { HideOn(((Component)hud.m_buildIcon).gameObject); } } private static GameObject BadgeHost(BuildUiPieceButton button) { if ((Object)(object)button == (Object)null) { return null; } object? obj = PieceButtonIconField?.GetValue(button); Image val = (Image)((obj is Image) ? obj : null); if (val != null && (Object)(object)val != (Object)null) { return ((Component)val).gameObject; } return ((Component)button).gameObject; } private static void HideOn(GameObject go) { Transform val = FindBadge(((Object)(object)go != (Object)null) ? go.transform : null); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } } private static Transform FindBadge(Transform root) { if ((Object)(object)root == (Object)null) { return null; } Transform val = root.Find("ComfortMeBadge"); if ((Object)(object)val != (Object)null) { return val; } for (int i = 0; i < root.childCount; i++) { Transform val2 = FindBadge(root.GetChild(i)); if ((Object)(object)val2 != (Object)null) { return val2; } } return null; } private static List<GameObject> GetIconObjects(Hud hud) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown List<GameObject> list = new List<GameObject>(); if (PieceIconsField?.GetValue(hud) is IList list2 && IconGoField != null) { for (int i = 0; i < list2.Count; i++) { object? value = IconGoField.GetValue(list2[i]); GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null) { list.Add(val); } } return list; } if ((Object)(object)hud != (Object)null && (Object)(object)hud.m_pieceListRoot != (Object)null) { foreach (Transform item in (Transform)hud.m_pieceListRoot) { Transform val2 = item; if ((Object)(object)val2 != (Object)null && ((Object)val2).name != "ComfortMeBadge") { list.Add(((Component)val2).gameObject); } } } return list; } private static void CleanupStrayBadges(BuildUi ui) { if ((Object)(object)ui == (Object)null) { return; } Transform[] componentsInChildren = ((Component)ui).GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if ((Object)(object)val == (Object)null || ((Object)val).name != "ComfortMeBadge") { continue; } if ((Object)(object)((Component)val).GetComponent<Canvas>() != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)val).gameObject); continue; } BuildUiPieceButton componentInParent = ((Component)val).GetComponentInParent<BuildUiPieceButton>(); GameObject val2 = (((Object)(object)componentInParent != (Object)null) ? BadgeHost(componentInParent) : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val.parent != (Object)(object)val2.transform) { Object.DestroyImmediate((Object)(object)((Component)val).gameObject); } } } private static void SetBadge(GameObject iconGo, TMP_FontAsset font, Material fontMaterial, bool visible, string label, Color color) { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) Transform val = FindBadge(iconGo.transform); if ((Object)(object)val != (Object)null && ((Object)(object)((Component)val).GetComponent<Canvas>() != (Object)null || (Object)(object)val.parent != (Object)(object)iconGo.transform)) { Object.DestroyImmediate((Object)(object)((Component)val).gameObject); val = null; } if (!visible) { if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } return; } Image val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Image>() : CreateBadge(iconGo, font, fontMaterial)); if (!((Object)(object)val2 == (Object)null)) { ((MaskableGraphic)val2).maskable = true; ((Component)val2).gameObject.SetActive(true); ((Component)val2).transform.SetAsLastSibling(); TextMeshProUGUI componentInChildren = ((Component)val2).GetComponentInChildren<TextMeshProUGUI>(true); if ((Object)(object)componentInChildren != (Object)null) { HudUi.ApplyVanillaFont((TMP_Text)(object)componentInChildren, Hud.instance); ((TMP_Text)componentInChildren).text = label; ((Graphic)componentInChildren).color = color; ((MaskableGraphic)componentInChildren).maskable = true; } } } private static Image CreateBadge(GameObject iconGo, TMP_FontAsset font, Material fontMaterial) { //IL_000b: 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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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) Image obj = HudUi.CreatePanel(iconGo.transform, "ComfortMeBadge", ChipBackground); RectTransform rectTransform = ((Graphic)obj).rectTransform; rectTransform.anchorMin = new Vector2(1f, 0f); rectTransform.anchorMax = new Vector2(1f, 0f); rectTransform.pivot = new Vector2(1f, 0f); rectTransform.sizeDelta = new Vector2(28f, 16f); rectTransform.anchoredPosition = new Vector2(-2f, 2f); ((Transform)rectTransform).SetAsLastSibling(); TextMeshProUGUI obj2 = HudUi.CreateText(((Component)obj).transform, "Text", font, fontMaterial, 11f); ((TMP_Text)obj2).alignment = (TextAlignmentOptions)514; ((Graphic)obj2).color = UpgradeGreen; RectTransform rectTransform2 = ((TMP_Text)obj2).rectTransform; rectTransform2.anchorMin = Vector2.zero; rectTransform2.anchorMax = Vector2.one; rectTransform2.offsetMin = Vector2.zero; rectTransform2.offsetMax = Vector2.zero; return obj; } } internal static class ComfortHudCursor { internal static bool ShouldFreeMouse() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !((Character)localPlayer).InPlaceMode()) { return false; } if (Hud.IsPieceSelectionVisible()) { return false; } KeyboardShortcut value = ModConfig.HudCursorKey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey != 0) { return ((KeyboardShortcut)(ref value)).IsPressed(); } return false; } internal static void FreeCursor() { ZCursor.LockState = (CursorLockMode)0; ZCursor.Show(); } } internal static class ComfortLinks { private static readonly FieldInfo StatusEffectsField = AccessTools.Field(typeof(Hud), "m_statusEffects"); private static readonly FieldInfo PlacementGhostField = AccessTools.Field(typeof(Player), "m_placementGhost"); private static readonly List<GameObject> beams = new List<GameObject>(); private static readonly List<RectTransform> restedIcons = new List<RectTransform>(); private static GameObject prefab; private static Transform beamRoot; private static bool loggedPrefab; private static bool loggedMissing; private static int suppressFrame = -1; private static bool suppressCached; internal static void Clear() { Hide(); restedIcons.Clear(); prefab = null; loggedPrefab = false; loggedMissing = false; suppressFrame = -1; if ((Object)(object)beamRoot != (Object)null && Object.op_Implicit((Object)(object)beamRoot)) { Object.Destroy((Object)(object)((Component)beamRoot).gameObject); } beamRoot = null; } internal static void BindRestedIcons(Hud hud, List<StatusEffect> effects) { restedIcons.Clear(); if ((Object)(object)hud == (Object)null || effects == null || !(StatusEffectsField?.GetValue(hud) is List<RectTransform> list)) { return; } int num = Mathf.Min(list.Count, effects.Count); for (int i = 0; i < num; i++) { if (effects[i] is SE_Rested) { RectTransform val = list[i]; if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy) { restedIcons.Add(val); } } } } internal static bool SuppressVanillaStationLines() { int frameCount = Time.frameCount; if (frameCount == suppressFrame) { return suppressCached; } suppressFrame = frameCount; suppressCached = ComputeSuppressVanillaStationLines(); return suppressCached; } internal static void Tick(Hud hud) { if (!ModConfig.Enabled.Value || !ModConfig.ShowComfortLinks.Value) { Hide(); return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Hide(); return; } Piece placementGhostPiece = GetPlacementGhostPiece(localPlayer); bool flag = ShouldDrawGhostLine(localPlayer, placementGhostPiece); bool flag2 = GroupCatalogPanel.IsHovered() || IsRestedIconHovered(); if (!flag2 && !flag) { Hide(); return; } ComfortSnapshot snapshot = (flag2 ? ComfortSnapshot.Capture(localPlayer) : null); Show(localPlayer, snapshot, flag ? placementGhostPiece : null); } internal static void Hide() { for (int i = 0; i < beams.Count; i++) { GameObject val = beams[i]; if ((Object)(object)val != (Object)null) { val.SetActive(false); } } } private static bool ComputeSuppressVanillaStationLines() { if (!ModConfig.Enabled.Value || !ModConfig.ShowComfortLinks.Value) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } Piece placementGhostPiece = GetPlacementGhostPiece(localPlayer); if (IsStationExtension(placementGhostPiece)) { return false; } if (ShouldDrawGhostLine(localPlayer, placementGhostPiece)) { return true; } if (!GroupCatalogPanel.IsHovered()) { return IsRestedIconHovered(); } return true; } private static bool IsRestedIconHovered() { //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_000a: 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) Vector2 screen = Vector2.op_Implicit(Input.mousePosition); for (int i = 0; i < restedIcons.Count; i++) { RectTransform val = restedIcons[i]; if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy && ContainsScreenPoint(val, screen)) { return true; } } return false; } private static bool ContainsScreenPoint(RectTransform rt, Vector2 screen) { //IL_0011: 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) Canvas componentInParent = ((Component)rt).GetComponentInParent<Canvas>(); Camera val = (((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null); return RectTransformUtility.RectangleContainsScreenPoint(rt, screen, val); } private static void Show(Player player, ComfortSnapshot snapshot, Piece ghost) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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_0027: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if (!ResolvePrefab()) { return; } Vector3 to = ((Component)player).transform.position + Vector3.up * 1.1f; int num = 0; if (snapshot != null) { List<Piece> countingPieces = snapshot.CountingPieces; for (int i = 0; i < countingPieces.Count; i++) { Piece val = countingPieces[i]; if (IsComfortLinkSource(val)) { num = PlaceBeam(num, ((Component)val).transform.position + Vector3.up * 0.4f, to); } } } if (IsComfortLinkSource(ghost)) { num = PlaceBeam(num, ((Component)ghost).transform.position + Vector3.up * 0.4f, to); } for (int j = num; j < beams.Count; j++) { if ((Object)(object)beams[j] != (Object)null) { beams[j].SetActive(false); } } } private static bool IsComfortLinkSource(Piece piece) { if ((Object)(object)piece == (Object)null || (Object)(object)((Component)piece).gameObject == (Object)null || !((Component)piece).gameObject.activeInHierarchy) { return false; } if (piece.m_comfort <= 0) { return false; } return !IsStationPiece(piece); } private static bool IsStationPiece(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } return (Object)(object)((Component)piece).GetComponent<StationExtension>() != (Object)null; } private static bool IsStationExtension(Piece piece) { if ((Object)(object)piece != (Object)null) { return (Object)(object)((Component)piece).GetComponent<StationExtension>() != (Object)null; } return false; } private static int PlaceBeam(int used, Vector3 from, Vector3 to) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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) Vector3 val = to - from; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 0.05f) { return used; } GameObject beam = GetBeam(used); Transform transform = beam.transform; transform.position = from; transform.rotation = Quaternion.LookRotation(val / magnitude); transform.localScale = new Vector3(1f, 1f, magnitude); beam.SetActive(true); PlayBeam(beam); return used + 1; } private static Piece GetPlacementGhostPiece(Player player) { if ((Object)(object)player == (Object)null || PlacementGhostField == null) { return null; } object value = PlacementGhostField.GetValue(player); Piece val = (Piece)((value is Piece) ? value : null); if (val != null) { if (!((Object)(object)((Component)val).gameObject != (Object)null) || !((Component)val).gameObject.activeInHierarchy) { return null; } return val; } GameObject val2 = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val2 == (Object)null || !val2.activeInHierarchy) { return null; } return val2.GetComponent<Piece>() ?? val2.GetComponentInChildren<Piece>(); } private static bool ShouldDrawGhostLine(Player player, Piece ghost) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || !IsComfortLinkSource(ghost)) { return false; } return Vector3.Distance(((Component)ghost).transform.position, ((Component)player).transform.position) <= 10f; } private static GameObject GetBeam(int index) { while (index >= beams.Count) { beams.Add(null); } GameObject val = beams[index]; if ((Object)(object)val == (Object)null) { EnsureBeamRoot(); val = Object.Instantiate<GameObject>(prefab); ((Object)val).name = "ComfortMeLink"; val.transform.SetParent(beamRoot, true); StripStationScripts(val); PrepareBeamVfx(val); beams[index] = val; } return val; } private static void EnsureBeamRoot() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (!((Object)(object)beamRoot != (Object)null) || !Object.op_Implicit((Object)(object)beamRoot)) { GameObject val = new GameObject("ComfortMeLinks"); Object.DontDestroyOnLoad((Object)val); beamRoot = val.transform; } } private static void StripStationScripts(GameObject beam) { StationExtension[] componentsInChildren = beam.GetComponentsInChildren<StationExtension>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } CraftingStation[] componentsInChildren2 = beam.GetComponentsInChildren<CraftingStation>(true); for (int j = 0; j < componentsInChildren2.Length; j++) { Object.Destroy((Object)(object)componentsInChildren2[j]); } Component[] componentsInChildren3 = beam.GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren3) { if ((Object)(object)val != (Object)null && ((object)val).GetType().Name == "TimedDestruction") { Object.Destroy((Object)(object)val); } } } private static void PrepareBeamVfx(GameObject beam) { //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) ParticleSystem[] componentsInChildren = beam.GetComponentsInChildren<ParticleSystem>(true); for (int i = 0; i < componentsInChildren.Length; i++) { MainModule main = componentsInChildren[i].main; ((MainModule)(ref main)).loop = true; ((MainModule)(ref main)).playOnAwake = true; } } private static void PlayBeam(GameObject beam) { ParticleSystem[] componentsInChildren = beam.GetComponentsInChildren<ParticleSystem>(true); for (int i = 0; i < componentsInChildren.Length; i++) { if (!componentsInChildren[i].isPlaying) { componentsInChildren[i].Play(true); } } } internal static string Describe() { Piece placementGhostPiece = GetPlacementGhostPiece(Player.m_localPlayer); int num = 0; for (int i = 0; i < beams.Count; i++) { if ((Object)(object)beams[i] != (Object)null && beams[i].activeSelf) { num++; } } string text = (((Object)(object)placementGhostPiece != (Object)null) ? (PieceDiscovery.LocalName(placementGhostPiece) + " +" + placementGhostPiece.m_comfort) : "none"); return "ComfortMe links: prefab=" + (((Object)(object)prefab != (Object)null) ? ((Object)prefab).name : "null") + " beams=" + num + "/" + beams.Count + " restedIcons=" + restedIcons.Count + " hoverCatalog=" + GroupCatalogPanel.IsHovered() + " hoverRested=" + IsRestedIconHovered() + " ghost=" + text + " suppressStation=" + SuppressVanillaStationLines(); } private static bool ResolvePrefab() { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab != (Object)null) { return true; } GameObject val = null; int num = int.MinValue; StationExtension[] array = Resources.FindObjectsOfTypeAll<StationExtension>(); foreach (StationExtension val2 in array) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.m_connectionPrefab == (Object)null)) { GameObject connectionPrefab = val2.m_connectionPrefab; int num2 = 0; Scene scene = connectionPrefab.scene; if (!((Scene)(ref scene)).IsValid()) { num2 += 10; } if (val2.m_continousConnection) { num2 += 50; } string name = ((Object)connectionPrefab).name; if (name.IndexOf("mage", StringComparison.OrdinalIgnoreCase) >= 0) { num2 += 40; } if (name.IndexOf("ExtensionConnection", StringComparison.OrdinalIgnoreCase) >= 0) { num2 += 5; } if (num2 > num) { num = num2; val = connectionPrefab; } } } prefab = val; if ((Object)(object)prefab != (Object)null) { if (!loggedPrefab) { loggedPrefab = true; ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)$"Comfort links using '{((Object)prefab).name}' (score {num})."); } } return true; } if (!loggedMissing) { loggedMissing = true; ManualLogSource log2 = ComfortMePlugin.Log; if (log2 != null) { log2.LogWarning((object)"Comfort links: no station connection prefab found yet."); } } return false; } } internal static class ComfortUsageTag { private const int FirstId = -100; private const int ShowAllId = -1; private const int FavoriteUiId = -10; private static readonly FieldInfo AvailableTagsField = AccessTools.Field(typeof(ByUsagePieceList), "m_availableTags"); private static readonly FieldInfo UsageTagsField = AccessTools.Field(typeof(ByUsagePieceList), "m_usageTags"); private static readonly FieldInfo CurrentTagIdField = AccessTools.Field(typeof(BuildUi), "m_currentTagId"); private static readonly MethodInfo SetCurrentTagMethod = AccessTools.Method(typeof(BuildUi), "SetCurrentTag", new Type[2] { typeof(int), typeof(bool) }, (Type[])null); private static int allocatedId; private static int previousId; private static bool loggedInsert; internal static string DisplayName => ModLocalization.T("comfortme_category"); internal static void Clear() { allocatedId = 0; previousId = 0; loggedInsert = false; } internal static bool IsAllocated(int tagId) { if (tagId != 0) { if (tagId != allocatedId) { return tagId == previousId; } return true; } return false; } internal static bool IsComfortTabSelected() { if (!ModConfig.ShowComfortCategory.Value || allocatedId == 0) { return false; } Hud instance = Hud.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_buildUi == (Object)null || !((Component)instance.m_buildUi).gameObject.activeInHierarchy) { return false; } if (CurrentTagIdField?.GetValue(instance.m_buildUi) is int tagId) { return IsAllocated(tagId); } return false; } internal static bool IsComfortRow(ByUsagePieceList list, int index) { List<int> list2 = AvailableTags(list); if (list2 != null && index >= 0 && index < list2.Count) { return IsAllocated(list2[index]); } return false; } internal static void Inject(ByUsagePieceList list, PieceTable table) { if (!ModConfig.Enabled.Value || !ModConfig.ShowComfortCategory.Value) { allocatedId = 0; return; } List<int> list2 = AvailableTags(list); if (list2 == null || !HasComfortPiece(table)) { allocatedId = 0; return; } int num = allocatedId; int num2 = ClaimId(list2); int num3 = InsertIndex(list, list2); list2.Insert(num3, num2); previousId = num; allocatedId = num2; if (!loggedInsert) { loggedInsert = true; ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)$"Comfort category row id={num2} at list index {num3}."); } } } internal static void RetargetSelection() { BuildUi val = (((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_buildUi : null); if ((Object)(object)val == (Object)null || CurrentTagIdField == null || allocatedId == 0) { previousId = allocatedId; return; } if (CurrentTagIdField.GetValue(val) is int num && previousId != 0 && num == previousId && num != allocatedId) { SetCurrentTagMethod?.Invoke(val, new object[2] { allocatedId, false }); } previousId = allocatedId; } internal static void Fill(PieceTable table, IList<Piece> resultOut) { if (resultOut == null) { return; } resultOut.Clear(); if ((Object)(object)table == (Object)null || table.m_availablePieces == null) { return; } List<Piece> list = new List<Piece>(); foreach (Piece availablePiece in table.m_availablePieces) { if (!((Object)(object)availablePiece == (Object)null) && availablePiece.m_comfort > 0 && !availablePiece.m_repairPiece && !availablePiece.m_removePiece) { list.Add(availablePiece); } } ComfortSnapshot snapshot = ComfortSnapshot.Capture(Player.m_localPlayer); list.Sort((Piece a, Piece b) => CompareComfort(a, b, snapshot)); for (int num = 0; num < list.Count; num++) { resultOut.Add(list[num]); } } private static List<int> AvailableTags(ByUsagePieceList list) { if (list != null) { return AvailableTagsField?.GetValue(list) as List<int>; } return null; } private static int ClaimId(List<int> tags) { int num = -100; while (num == -1 || num == -10 || tags.Contains(num)) { num--; if (num < -1000000) { break; } } return num; } private static int InsertIndex(ByUsagePieceList list, List<int> tags) { if (!(UsageTagsField?.GetValue(list) is UsageTagFlags[] array)) { return tags.Count; } int num = -1; for (int i = 0; i < array.Length; i++) { if ((int)array[i] == 128) { num = i; break; } } if (num < 0) { return tags.Count; } int num2 = tags.IndexOf(num); if (num2 < 0) { return tags.Count; } return num2 + 1; } private static bool HasComfortPiece(PieceTable table) { if ((Object)(object)table == (Object)null || table.m_availablePieces == null) { return false; } foreach (Piece availablePiece in table.m_availablePieces) { if ((Object)(object)availablePiece != (Object)null && availablePiece.m_comfort > 0 && !availablePiece.m_repairPiece && !availablePiece.m_removePiece) { return true; } } return false; } private static int CompareComfort(Piece a, Piece b, ComfortSnapshot snapshot) { int num = ComfortClassification.ShopRank(UpgradeClassifier.Classify(a, snapshot).Verdict).CompareTo(ComfortClassification.ShopRank(UpgradeClassifier.Classify(b, snapshot).Verdict)); if (num != 0) { return num; } int num2 = b.m_comfort.CompareTo(a.m_comfort); if (num2 != 0) { return num2; } return string.CompareOrdinal(PieceDiscovery.LocalName(a), PieceDiscovery.LocalName(b)); } } internal static class GroupCatalogPanel { private const string PanelName = "ComfortMeCatalog"; private const int OverlaySorting = 5000; private static readonly Color PanelBackground = new Color(0.04f, 0.05f, 0.05f, 0.92f); private static readonly Color HeaderColor = new Color(0.92f, 0.93f, 0.9f, 1f); private static Image panel; private static TextMeshProUGUI header; private static TextMeshProUGUI body; private static Canvas overlay; private static string lastText; private static bool loggedCreate; private static bool loggedPlace; internal static void Hide() { if ((Object)(object)panel != (Object)null) { ((Component)panel).gameObject.SetActive(false); } if ((Object)(object)overlay != (Object)null && Object.op_Implicit((Object)(object)overlay)) { ((Component)overlay).gameObject.SetActive(false); } lastText = null; } internal static bool IsHovered() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)panel == (Object)null || !((Component)panel).gameObject.activeInHierarchy) { return false; } Canvas canvas = ((Graphic)panel).canvas; Camera val = (((Object)(object)canvas != (Object)null && (int)canvas.renderMode != 0) ? canvas.worldCamera : null); return RectTransformUtility.RectangleContainsScreenPoint(((Graphic)panel).rectTransform, Vector2.op_Implicit(Input.mousePosition), val); } internal static void Clear() { lastText = null; loggedCreate = false; loggedPlace = false; header = null; body = null; if ((Object)(object)panel != (Object)null && Object.op_Implicit((Object)(object)panel)) { Object.Destroy((Object)(object)((Component)panel).gameObject); } panel = null; if ((Object)(object)overlay != (Object)null && Object.op_Implicit((Object)(object)overlay)) { Object.Destroy((Object)(object)((Component)overlay).gameObject); } overlay = null; } internal static void InvalidateText() { lastText = null; } internal static void Dump() { //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0174: 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_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) string text; if ((Object)(object)panel == (Object)null || !Object.op_Implicit((Object)(object)panel)) { text = "ComfortMe catalog: panel is null (hammer menu may be closed)."; } else { RectTransform rectTransform = ((Graphic)panel).rectTransform; Transform parent = ((Component)panel).transform.parent; string text2 = (((Object)(object)body != (Object)null && !string.IsNullOrEmpty(((TMP_Text)body).text)) ? ((TMP_Text)body).text.Replace('\n', '|') : ""); if (text2.Length > 160) { text2 = text2.Substring(0, 160); } string[] obj = new string[25] { "ComfortMe catalog: active=", ((Component)panel).gameObject.activeInHierarchy.ToString(), " overlay=", ((Object)(object)overlay != (Object)null && Object.op_Implicit((Object)(object)overlay)) ? overlay.sortingOrder.ToString() : "null", " parent=", ((Object)(object)parent != (Object)null) ? ((Object)parent).name : "null", " anchors=", ((object)rectTransform.anchorMin/*cast due to .constrained prefix*/).ToString(), "->", ((object)rectTransform.anchorMax/*cast due to .constrained prefix*/).ToString(), " pos=", ((object)rectTransform.anchoredPosition/*cast due to .constrained prefix*/).ToString(), " size=", null, null, null, null, null, null, null, null, null, null, null, null }; Rect rect = rectTransform.rect; obj[13] = ((object)((Rect)(ref rect)).size/*cast due to .constrained prefix*/).ToString(); obj[14] = " screen="; obj[15] = Screen.width.ToString(); obj[16] = "x"; obj[17] = Screen.height.ToString(); obj[18] = " font="; obj[19] = (((Object)(object)header != (Object)null && (Object)(object)((TMP_Text)header).font != (Object)null) ? ((Object)((TMP_Text)header).font).name : "null"); obj[20] = " header='"; obj[21] = (((Object)(object)header != (Object)null) ? ((TMP_Text)header).text : ""); obj[22] = "' body='"; obj[23] = text2; obj[24] = "'"; text = string.Concat(obj); } ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)text); } if ((Object)(object)Console.instance != (Object)null) { Console.instance.Print(text); } string text3 = ComfortLinks.Describe(); ManualLogSource log2 = ComfortMePlugin.Log; if (log2 != null) { log2.LogInfo((object)text3); } if ((Object)(object)Console.instance != (Object)null) { Console.instance.Print(text3); } ComfortSnapshot comfortSnapshot = ComfortSnapshot.Capture(Player.m_localPlayer); if (comfortSnapshot == null || comfortSnapshot.CountingPieces.Count == 0) { return; } List<string> list = new List<string>(comfortSnapshot.CountingPieces.Count); for (int i = 0; i < comfortSnapshot.CountingPieces.Count; i++) { Piece val = comfortSnapshot.CountingPieces[i]; if (!((Object)(object)val == (Object)null)) { list.Add(PieceDiscovery.LocalName(val)); } } string text4 = "ComfortMe counting: " + string.Join(", ", list.ToArray()); ManualLogSource log3 = ComfortMePlugin.Log; if (log3 != null) { log3.LogInfo((object)text4); } if ((Object)(object)Console.instance != (Object)null) { Console.instance.Print(text4); } } internal static void RefreshFromHud(Hud hud, ComfortSnapshot snapshot) { Refresh(hud, snapshot); } internal static void Refresh(Hud hud, ComfortSnapshot snapshot) { if ((Object)(object)hud == (Object)null || snapshot == null || !ModConfig.Enabled.Value || !ModConfig.ShowGroupCatalog.Value || !HudUi.HoldingPlaceTool()) { Hide(); return; } EnsurePanel(hud); if ((Object)(object)panel == (Object)null) { return; } if ((Object)(object)overlay != (Object)null && Object.op_Implicit((Object)(object)overlay)) { ((Component)overlay).gameObject.SetActive(true); } PlaceBesideBuildUi(hud); HudUi.ApplyVanillaFont((TMP_Text)(object)header, hud); HudUi.ApplyVanillaFont((TMP_Text)(object)body, hud); ((Component)panel).gameObject.SetActive(true); ModLocalization.EnsureApplied(); StringBuilder stringBuilder = new StringBuilder(256); stringBuilder.Append(ModLocalization.T("comfortme_this_room")).Append(": ").Append(snapshot.Total); if (!snapshot.InShelter) { stringBuilder.Append(" ").Append(ModLocalization.T("comfortme_unsheltered")); } stringBuilder.Append('\n'); stringBuilder.Append(ModLocalization.T("comfortme_base")).Append(" +1"); if (snapshot.InShelter) { stringBuilder.Append('\n').Append(ModLocalization.T("comfortme_shelter")).Append(" +1"); } else { stringBuilder.Append('\n').Append(ModLocalization.T("comfortme_shelter")).Append(" ") .Append(ModLocalization.T("comfortme_missing")); } AppendCounting(stringBuilder, snapshot); string text = stringBuilder.ToString(); if (!(text == lastText)) { lastText = text; int num = text.IndexOf('\n'); if ((Object)(object)header != (Object)null) { ((TMP_Text)header).text = ((num >= 0) ? text.Substring(0, num) : text); } if ((Object)(object)body != (Object)null) { ((TMP_Text)body).text = ((num >= 0) ? text.Substring(num + 1) : string.Empty); ((Component)body).gameObject.SetActive(true); } LayoutRebuilder.ForceRebuildLayoutImmediate(((Graphic)panel).rectTransform); } } private static void AppendCounting(StringBuilder builder, ComfortSnapshot snapshot) { //IL_0043: 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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) List<KeyValuePair<ComfortGroup, int>> list = new List<KeyValuePair<ComfortGroup, int>>(snapshot.GroupMax); list.Sort((KeyValuePair<ComfortGroup, int> a, KeyValuePair<ComfortGroup, int> b) => b.Value.CompareTo(a.Value)); for (int num = 0; num < list.Count; num++) { ComfortGroup key = list[num].Key; int value = list[num].Value; snapshot.GroupSource.TryGetValue(key, out var value2); builder.Append('\n'); builder.Append(string.IsNullOrEmpty(value2) ? PieceDiscovery.GroupLabel(key) : value2); builder.Append(" +").Append(value); builder.Append(" (").Append(PieceDiscovery.GroupLabel(key)).Append(')'); } for (int num2 = 0; num2 < snapshot.Uniques.Count; num2++) { UniqueComfort uniqueComfort = snapshot.Uniques[num2]; builder.Append('\n'); builder.Append(uniqueComfort.Name).Append(" +").Append(uniqueComfort.Comfort); } } private static void EnsurePanel(Hud hud) { //IL_00b1: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)panel != (Object)null && !Object.op_Implicit((Object)(object)panel)) { panel = null; header = null; body = null; lastText = null; } Transform val = ResolveParent(hud); if ((Object)(object)panel != (Object)null && (Object)(object)val != (Object)null && (Object)(object)((Component)panel).transform.parent != (Object)(object)val) { Object.Destroy((Object)(object)((Component)panel).gameObject); panel = null; header = null; body = null; lastText = null; } if ((Object)(object)panel != (Object)null || (Object)(object)val == (Object)null) { return; } TMP_FontAsset font = HudUi.FontFrom(hud); panel = HudUi.CreatePanel(val, "ComfortMeCatalog", PanelBackground); ((MaskableGraphic)panel).maskable = false; RectTransform rectTransform = ((Graphic)panel).rectTransform; rectTransform.sizeDelta = new Vector2(320f, 0f); ((Transform)rectTransform).SetAsLastSibling(); VerticalLayoutGroup obj = ((Component)panel).gameObject.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)obj).padding = new RectOffset(10, 10, 8, 8); ((HorizontalOrVerticalLayoutGroup)obj).spacing = 3f; ((LayoutGroup)obj).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true; ContentSizeFitter obj2 = ((Component)panel).gameObject.AddComponent<ContentSizeFitter>(); obj2.horizontalFit = (FitMode)0; obj2.verticalFit = (FitMode)2; header = HudUi.CreateText(((Component)panel).transform, "Header", font, null, 16f); ((TMP_Text)header).alignment = (TextAlignmentOptions)257; ((Graphic)header).color = HeaderColor; ((MaskableGraphic)header).maskable = false; ((TMP_Text)header).textWrappingMode = (TextWrappingModes)1; LayoutElement obj3 = ((Component)header).gameObject.AddComponent<LayoutElement>(); obj3.minWidth = 280f; obj3.minHeight = 22f; HudUi.ApplyVanillaFont((TMP_Text)(object)header, hud); body = HudUi.CreateText(((Component)panel).transform, "Body", font, null, 14f); ((TMP_Text)body).alignment = (TextAlignmentOptions)257; ((Graphic)body).color = HeaderColor; ((MaskableGraphic)body).maskable = false; ((TMP_Text)body).textWrappingMode = (TextWrappingModes)1; LayoutElement obj4 = ((Component)body).gameObject.AddComponent<LayoutElement>(); obj4.minWidth = 280f; obj4.minHeight = 40f; HudUi.ApplyVanillaFont((TMP_Text)(object)body, hud); if (!loggedCreate) { loggedCreate = true; ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)"Catalog shown on overlay canvas (room breakdown)."); } } PlaceBesideBuildUi(hud); } private static void PlaceBesideBuildUi(Hud hud) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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_00a9: 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_00b1: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)panel == (Object)null) && !((Object)(object)hud == (Object)null)) { RectTransform rectTransform = ((Graphic)panel).rectTransform; Transform parent = ((Transform)rectTransform).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (!((Object)(object)val == (Object)null)) { rectTransform.sizeDelta = new Vector2(320f, 0f); ((Transform)rectTransform).SetAsLastSibling(); rectTransform.pivot = new Vector2(0f, 1f); rectTransform.anchorMin = new Vector2(0.84f, 0.78f); rectTransform.anchorMax = new Vector2(0.84f, 0.78f); rectTransform.anchoredPosition = Vector2.zero; string name = ((Object)val).name; Rect rect = val.rect; LogPlaceOnce($"Catalog dock 0.84,0.78 parent={name} size={((Rect)(ref rect)).size}"); } } } private static void LogPlaceOnce(string message) { if (!loggedPlace) { loggedPlace = true; ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)message); } } } private static Transform ResolveParent(Hud hud) { EnsureOverlay(hud); if (!((Object)(object)overlay != (Object)null)) { return null; } return ((Component)overlay).transform; } private static void EnsureOverlay(Hud hud) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)overlay != (Object)null && !Object.op_Implicit((Object)(object)overlay)) { overlay = null; } if ((Object)(object)overlay != (Object)null) { ((Component)overlay).gameObject.SetActive(true); return; } Canvas val = null; if ((Object)(object)hud.m_rootObject != (Object)null) { val = hud.m_rootObject.GetComponent<Canvas>() ?? hud.m_rootObject.GetComponentInParent<Canvas>(); } Transform val2 = (((Object)(object)val != (Object)null && (Object)(object)((Component)val).transform.parent != (Object)null) ? ((Component)val).transform.parent : ((Component)hud).transform); GameObject val3 = new GameObject("ComfortMeOverlay") { layer = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject.layer : 5) }; val3.transform.SetParent(val2, false); overlay = val3.AddComponent<Canvas>(); overlay.renderMode = (RenderMode)0; overlay.overrideSorting = true; overlay.sortingOrder = 5000; overlay.pixelPerfect = false; CanvasScaler val4 = val3.AddComponent<CanvasScaler>(); CanvasScaler val5 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<CanvasScaler>() : null); if ((Object)(object)val5 != (Object)null) { val4.uiScaleMode = val5.uiScaleMode; val4.referenceResolution = val5.referenceResolution; val4.screenMatchMode = val5.screenMatchMode; val4.matchWidthOrHeight = val5.matchWidthOrHeight; val4.referencePixelsPerUnit = val5.referencePixelsPerUnit; val4.scaleFactor = val5.scaleFactor; } else { val4.uiScaleMode = (ScaleMode)1; val4.referenceResolution = new Vector2(1920f, 1080f); val4.screenMatchMode = (ScreenMatchMode)0; val4.matchWidthOrHeight = 1f; } ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)string.Format("Catalog overlay canvas sorting={0} parent={1} hudCanvas={2}", 5000, ((Object)val2).name, ((Object)(object)val != (Object)null) ? ((Object)val).name : "null")); } } } internal static class GroupHoverHighlight { private const string OverlayName = "ComfortMeGroupPlate"; private static readonly Color Lift = new Color(1f, 1f, 1f, 0.28f); private static readonly FieldInfo HoveredField = AccessTools.Field(typeof(BuildUi), "m_currentHoveredPieceButton"); private static readonly FieldInfo ButtonsField = AccessTools.Field(typeof(BuildUi), "m_pieceButtons"); private static readonly FieldInfo ButtonField = AccessTools.Field(typeof(BuildUiPieceButton), "m_button"); private static readonly FieldInfo IconField = AccessTools.Field(typeof(BuildUiPieceButton), "m_icon"); private static readonly FieldInfo UpgradeArrowField = AccessTools.Field(typeof(BuildUiPieceButton), "m_upgradeArrow"); private static readonly FieldInfo FavoriteStarField = AccessTools.Field(typeof(BuildUiPieceButton), "m_favoriteStar"); private static bool loggedPlate; internal static void Clear() { loggedPlate = false; HideAll(); } internal static void Tick() { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Invalid comparison between Unknown and I4 //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value || !ModConfig.ShowGroupHoverHighlight.Value || !ComfortUsageTag.IsComfortTabSelected()) { HideAll(); return; } Hud instance = Hud.instance; BuildUi val = (((Object)(object)instance != (Object)null) ? instance.m_buildUi : null); if ((Object)(object)val == (Object)null) { HideAll(); return; } object? obj = HoveredField?.GetValue(val); BuildUiPieceButton val2 = (BuildUiPieceButton)((obj is BuildUiPieceButton) ? obj : null); Piece val3 = (((Object)(object)val2 != (Object)null) ? val2.Piece : null); ComfortGroup val4 = (ComfortGroup)(((Object)(object)val3 != (Object)null) ? ((int)val3.m_comfortGroup) : 0); bool flag = (Object)(object)val3 != (Object)null && val3.m_comfort > 0 && (int)val4 > 0; if (!(ButtonsField?.GetValue(val) is IList list)) { return; } for (int i = 0; i < list.Count; i++) { object? obj2 = list[i]; BuildUiPieceButton val5 = (BuildUiPieceButton)((obj2 is BuildUiPieceButton) ? obj2 : null); if (val5 != null && !((Object)(object)val5 == (Object)null)) { Piece piece = val5.Piece; bool lifted = flag && (Object)(object)piece != (Object)null && piece.m_comfort > 0 && piece.m_comfortGroup == val4; SetLift(val5, lifted); } } } private static void HideAll() { Hud instance = Hud.instance; BuildUi val = (((Object)(object)instance != (Object)null) ? instance.m_buildUi : null); if ((Object)(object)val == (Object)null || !(ButtonsField?.GetValue(val) is IList list)) { return; } for (int i = 0; i < list.Count; i++) { object? obj = list[i]; BuildUiPieceButton val2 = (BuildUiPieceButton)((obj is BuildUiPieceButton) ? obj : null); if (val2 != null && (Object)(object)val2 != (Object)null) { SetLift(val2, lifted: false); } } } private static void SetLift(BuildUiPieceButton button, bool lifted) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) Image val = ResolvePlate(button); if ((Object)(object)val == (Object)null) { return; } Transform val2 = ((Component)val).transform.Find("ComfortMeGroupPlate"); if (!lifted) { if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(false); } return; } Image val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<Image>() : null); if ((Object)(object)val3 == (Object)null) { val3 = HudUi.CreatePanel(((Component)val).transform, "ComfortMeGroupPlate", Lift); RectTransform rectTransform = ((Graphic)val3).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; ((Graphic)val3).raycastTarget = false; ((MaskableGraphic)val3).maskable = true; if (!loggedPlate) { loggedPlate = true; ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)("Group hover plate '" + ((Object)val).name + "' on '" + ((Object)button).name + "'.")); } } } val3.sprite = (((Object)(object)val.sprite != (Object)null) ? val.sprite : HudUi.WhiteSprite); val3.type = val.type; ((Graphic)val3).color = Lift; ((Component)val3).gameObject.SetActive(true); ((Component)val3).transform.SetAsFirstSibling(); } private static Image ResolvePlate(BuildUiPieceButton button) { object? obj = IconField?.GetValue(button); Image icon = (Image)((obj is Image) ? obj : null); object? obj2 = ButtonField?.GetValue(button); Button val = (Button)((obj2 is Button) ? obj2 : null); if (val != null) { Graphic targetGraphic = ((Selectable)val).targetGraphic; Image val2 = (Image)(object)((targetGraphic is Image) ? targetGraphic : null); if (val2 != null && !IsForbidden(val2, button, icon)) { return val2; } } Image component = ((Component)button).GetComponent<Image>(); if ((Object)(object)component != (Object)null && !IsForbidden(component, button, icon)) { return component; } Image[] componentsInChildren = ((Component)button).GetComponentsInChildren<Image>(true); foreach (Image val3 in componentsInChildren) { if (!IsForbidden(val3, button, icon)) { return val3; } } return null; } private static bool IsForbidden(Image image, BuildUiPieceButton button, Image icon) { if ((Object)(object)image == (Object)null) { return true; } if ((Object)(object)image == (Object)(object)icon || ((Object)image).name == "ComfortMeGroupPlate" || ((Object)image).name == "ComfortMeBadge") { return true; } object? obj = UpgradeArrowField?.GetValue(button); Image val = (Image)((obj is Image) ? obj : null); if (val != null && (Object)(object)image == (Object)(object)val) { return true; } object? obj2 = FavoriteStarField?.GetValue(button); Image val2 = (Image)((obj2 is Image) ? obj2 : null); if (val2 != null && (Object)(object)image == (Object)(object)val2) { return true; } return false; } } internal static class HudUi { private static Sprite whiteSprite; private static TMP_FontAsset cachedFont; internal static Sprite WhiteSprite { get { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)whiteSprite == (Object)null) { Texture2D whiteTexture = Texture2D.whiteTexture; whiteSprite = Sprite.Create(whiteTexture, new Rect(0f, 0f, (float)((Texture)whiteTexture).width, (float)((Texture)whiteTexture).height), new Vector2(0.5f, 0.5f), 100f); } return whiteSprite; } } internal static TMP_FontAsset FontFrom(Hud hud) { Resolve(hud); return cachedFont; } internal static Material FontMaterialFrom(Hud hud) { return null; } internal static bool BuildMenuOpen(Hud hud) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !((Character)localPlayer).InPlaceMode()) { return false; } if ((Object)(object)hud != (Object)null && (Object)(object)hud.m_buildUi != (Object)null && ((Component)hud.m_buildUi).gameObject.activeInHierarchy) { return true; } if (Hud.IsPieceSelectionVisible()) { return true; } if ((Object)(object)hud != (Object)null && (Object)(object)hud.m_buildHud != (Object)null) { return hud.m_buildHud.activeInHierarchy; } return false; } internal static bool HoldingPlaceTool() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { return ((Character)localPlayer).InPlaceMode(); } return false; } internal static void ApplyVanillaFont(TMP_Text dest, Hud hud) { if (!((Object)(object)dest == (Object)null)) { Resolve(hud); if ((Object)(object)cachedFont != (Object)null) { dest.font = cachedFont; } } } internal static TextMeshProUGUI CreateText(Transform parent, string name, TMP_FontAsset font, Material fontMaterial, float size) { //IL_0014: 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_002a: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }) { layer = ((Component)parent).gameObject.layer }; val.transform.SetParent(parent, false); TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>(); TMP_FontAsset val3 = (((Object)(object)font != (Object)null) ? font : cachedFont); if ((Object)(object)val3 != (Object)null) { ((TMP_Text)val2).font = val3; } ((TMP_Text)val2).fontSize = size; ((Graphic)val2).raycastTarget = false; ((MaskableGraphic)val2).maskable = true; ((TMP_Text)val2).overflowMode = (TextOverflowModes)0; ((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)val2).richText = true; return val2; } internal static Image CreatePanel(Transform parent, string name, Color color) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }) { layer = ((Component)parent).gameObject.layer }; val.transform.SetParent(parent, false); Image component = val.GetComponent<Image>(); component.sprite = WhiteSprite; ((Graphic)component).color = color; ((Graphic)component).raycastTarget = false; ((MaskableGraphic)component).maskable = true; return component; } internal static string Hex(Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return ColorUtility.ToHtmlStringRGB(color); } private static void Resolve(Hud hud) { if ((Object)(object)cachedFont != (Object)null) { return; } TMP_Text val = FindSource(hud); if ((Object)(object)val != (Object)null && (Object)(object)val.font != (Object)null) { cachedFont = val.font; ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)("Using HUD font '" + ((Object)cachedFont).name + "'.")); } return; } TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); foreach (TMP_FontAsset val2 in array) { if (!((Object)(object)val2 == (Object)null) && (((Object)val2).name ?? string.Empty).IndexOf("Liberation", StringComparison.OrdinalIgnoreCase) < 0) { cachedFont = val2; ManualLogSource log2 = ComfortMePlugin.Log; if (log2 != null) { log2.LogInfo((object)("Using fallback font '" + ((Object)cachedFont).name + "'.")); } break; } } } private static TMP_Text FindSource(Hud hud) { if ((Object)(object)hud == (Object)null) { return null; } if (IsUsable(hud.m_buildSelection)) { return hud.m_buildSelection; } if (IsUsable(hud.m_pieceDescription)) { return hud.m_pieceDescription; } TMP_Text[] componentsInChildren = ((Component)hud).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if (IsUsable(val) && !IsOurs(val)) { return val; } } return null; } private static bool IsUsable(TMP_Text text) { if ((Object)(object)text != (Object)null) { return (Object)(object)text.font != (Object)null; } return false; } private static bool IsOurs(TMP_Text text) { string name = ((Object)text).name; if (!(name == "Header") && !(name == "Body")) { return name == "Text"; } return true; } } internal static class ModLocalization { private const string FolderName = "translations"; private const int DownloadTimeoutMs = 8000; private static readonly Dictionary<string, string> english = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly object downloadLock = new object(); private static readonly HashSet<string> queuedDownloads = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static string appliedLanguage; private static bool hooked; private static bool loggedFolder; private static volatile bool pendingReload; private static volatile bool englishFetched; internal static void Hook() { if (!hooked) { hooked = true; Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(ApplySelected)); ApplySelected(); } } internal static void EnsureApplied() { if (Localization.instance == null) { return; } if (pendingReload) { pendingReload = false; english.Clear(); appliedLanguage = null; ApplySelected(); } else { string text = CurrentLanguage(); if (!(appliedLanguage == text) || !HasOurWord()) { ApplySelected(); } } } internal static string T(string key) { EnsureApplied(); string result = Fallback(key); if (Localization.instance == null) { return result; } string text = Localization.instance.Localize("$" + key); if (string.IsNullOrEmpty(text) || text == "$" + key) { return result; } return text; } internal static void ApplySelected() { QueueDownload(); string text = CurrentLanguage(); Dictionary<string, string> dictionary = LoadMerged(text); Localization instance = Localization.instance; if (instance == null) { return; } foreach (KeyValuePair<string, string> item in dictionary) { SetWord(instance, item.Key, item.Value); } appliedLanguage = text; GroupCatalogPanel.InvalidateText(); ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)$"Localization applied ({text}, {dictionary.Count} string(s))."); } } private static string CurrentLanguage() { if (Localization.instance == null) { return "English"; } string selectedLanguage = Localization.instance.GetSelectedLanguage(); if (!string.IsNullOrEmpty(selectedLanguage)) { return selectedLanguage; } return "English"; } private static Dictionary<string, string> LoadMerged(string language) { if (english.Count == 0) { LoadFile("English", english); } Dictionary<string, string> dictionary = new Dictionary<string, string>(english, StringComparer.Ordinal); string text = MapLanguageFile(language); if (!string.Equals(text, "English", StringComparison.OrdinalIgnoreCase)) { Dictionary<string, string> dictionary2 = new Dictionary<string, string>(StringComparer.Ordinal); LoadFile(text, dictionary2); foreach (KeyValuePair<string, string> item in dictionary2) { dictionary[item.Key] = item.Value; } } return dictionary; } private static string MapLanguageFile(string language) { if (string.Equals(language, "Chinese_Simplified", StringComparison.OrdinalIgnoreCase) || string.Equals(language, "ChineseSimplified", StringComparison.OrdinalIgnoreCase) || string.Equals(language, "SChinese", StringComparison.OrdinalIgnoreCase)) { return "Chinese"; } return language; } private static void QueueDownload() { if (ModConfig.DownloadTranslations == null || !ModConfig.DownloadTranslations.Value) { return; } string text = ((ModConfig.TranslationsUrl != null) ? ModConfig.TranslationsUrl.Value : null); if (string.IsNullOrEmpty(text)) { return; } string language = MapLanguageFile(CurrentLanguage()); if (!IsSafeLanguageName(language)) { return; } lock (downloadLock) { if (!queuedDownloads.Add(language)) { return; } } string url = text.TrimEnd(new char[1] { '/' }) + "/"; ThreadPool.QueueUserWorkItem(delegate { FetchFromGitHub(url, language); }); } private static void FetchFromGitHub(string baseUrl, string language) { try { TryEnableTls12(); bool flag = false; if (!englishFetched && TryDownloadFile(baseUrl, "English")) { englishFetched = true; flag = true; } if (!string.Equals(language, "English", StringComparison.OrdinalIgnoreCase) && TryDownloadFile(baseUrl, language)) { flag = true; } if (flag) { pendingReload = true; } } catch (Exception ex) { ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)("Translation download skipped: " + ex.Message)); } } } private static bool TryDownloadFile(string baseUrl, string language) { if (!IsSafeLanguageName(language)) { return false; } string requestUriString = baseUrl + language + ".json"; try { HttpWebRequest obj = (HttpWebRequest)WebRequest.Create(requestUriString); obj.Method = "GET"; obj.UserAgent = "ComfortMe/1.3.1"; obj.Timeout = 8000; obj.ReadWriteTimeout = 8000; obj.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; using HttpWebResponse httpWebResponse = (HttpWebResponse)obj.GetResponse(); using Stream stream = httpWebResponse.GetResponseStream(); if (httpWebResponse.StatusCode != HttpStatusCode.OK || stream == null) { return false; } string text; using (StreamReader streamReader = new StreamReader(stream, Encoding.UTF8)) { text = streamReader.ReadToEnd(); } Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); ParseObject(text, dictionary); if (dictionary.Count == 0) { ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)("GitHub " + language + ".json was empty or invalid; keeping local file.")); } return false; } string text2 = CacheDirectory(); Directory.CreateDirectory(text2); File.WriteAllText(Path.Combine(text2, language + ".json"), text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); ManualLogSource log2 = ComfortMePlugin.Log; if (log2 != null) { log2.LogInfo((object)$"Updated translations from GitHub: {language}.json ({dictionary.Count} string(s))."); } return true; } catch (WebException ex) { if (ex.Response is HttpWebResponse { StatusCode: HttpStatusCode.NotFound }) { ManualLogSource log3 = ComfortMePlugin.Log; if (log3 != null) { log3.LogInfo((object)("No GitHub translations for " + language + " (using local files).")); } } else { ManualLogSource log4 = ComfortMePlugin.Log; if (log4 != null) { log4.LogInfo((object)("Translation download skipped (" + language + "): " + ex.Message)); } } return false; } catch (Exception ex2) { ManualLogSource log5 = ComfortMePlugin.Log; if (log5 != null) { log5.LogInfo((object)("Translation download skipped (" + language + "): " + ex2.Message)); } return false; } } private static string CacheDirectory() { return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "translations"); } private static bool IsSafeLanguageName(string language) { if (string.IsNullOrEmpty(language) || language.Length > 64) { return false; } foreach (char c in language) { if (!char.IsLetterOrDigit(c) && c != '_' && c != '-') { return false; } } return true; } private static void TryEnableTls12() { try { ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; } catch { } } private static void LoadFile(string language, Dictionary<string, string> dest) { dest.Clear(); string text = FindFile(language); if (text == null) { if (!string.Equals(language, "English", StringComparison.OrdinalIgnoreCase)) { ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogWarning((object)("Localization file '" + language + ".json' was not found.")); } } } else { ParseObject(File.ReadAllText(text, Encoding.UTF8), dest); } } private static string FindFile(string language) { string path = language + ".json"; string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string[] array = new string[3] { directoryName, Path.Combine(directoryName ?? string.Empty, "translations"), Path.GetFullPath(Path.Combine(directoryName ?? ".", "..", "..", "..", "translations")) }; string text = null; foreach (string text2 in array) { if (!string.IsNullOrEmpty(text2) && Directory.Exists(text2)) { if (!loggedFolder) { text = text2; } string text3 = Path.Combine(text2, path); if (File.Exists(text3)) { loggedFolder = true; return text3; } } } if (!loggedFolder && text != null) { loggedFolder = true; ManualLogSource log = ComfortMePlugin.Log; if (log != null) { log.LogInfo((object)("Localization folder: " + text)); } } return null; } private static void SetWord(Localization loc, string key, string value) { MethodInfo methodInfo = AccessTools.Method(typeof(Localization), "AddWord", new Type[2] { typeof(string), typeof(string) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(loc, new object[2] { key, value }); return; } Dictionary<string, string> dictionary = null; foreach (FieldInfo declaredField in AccessTools.GetDeclaredFields(typeof(Localization))) { if (!(declaredField.FieldType != typeof(Dictionary<string, string>)) && declaredField.GetValue(loc) is Dictionary<string, string> dictionary2 && (dictionary == null || dictionary2.Count > dictionary.Count)) { dictionary = dictionary2; } }