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 CosmeticArmor v1.0.2
CosmeticArmor.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExtraSlots; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("CosmeticArmor")] [assembly: AssemblyDescription("ExtraSlots add-on with cosmetic armor slots that change how your armor looks")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: InternalsVisibleTo("CosmeticCheck")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CosmeticArmor { internal enum Part { Helmet, Chest, Legs, Cape, Utility, Trinket } internal enum Look { Cosmetic, Armor, Hidden } internal static class CosmeticSlots { internal static readonly Part[] Parts = new Part[6] { Part.Helmet, Part.Chest, Part.Legs, Part.Cape, Part.Utility, Part.Trinket }; internal const string LooksKey = "CosmeticArmor_Looks"; internal static Func<Part, ItemData> SlotItem = delegate(Part part) { Slot obj = API.FindSlot(SlotId(part)); return (obj == null) ? null : obj.Item; }; internal static Func<Dictionary<string, string>> SavedData = () => Player.m_localPlayer?.m_customData; internal static Func<ItemData, bool> IsWorn = delegate(ItemData item) { Player localPlayer = Player.m_localPlayer; return localPlayer != null && ((Humanoid)localPlayer).IsItemEquiped(item); }; private static readonly string[] SlotIds = Parts.Select((Part p) => "CosmeticArmor_" + p).ToArray(); private static string _parsedFrom; private static readonly Look[] Parsed = new Look[Parts.Length]; internal static string SlotId(Part part) { return SlotIds[(int)part]; } internal static ItemType TypeOf(Part part) { return (ItemType)(part switch { Part.Helmet => 6, Part.Chest => 7, Part.Legs => 11, Part.Utility => 18, Part.Trinket => 24, _ => 17, }); } internal static string SlotName(Part part) { if (!Layout.GroupPlaced) { return NameOf(part); } return part.ToString(); } internal static string NameOf(Part part) { return part switch { Part.Helmet => "Cosmetic helmet", Part.Chest => "Cosmetic chest", Part.Legs => "Cosmetic legs", Part.Utility => "Cosmetic utility", Part.Trinket => "Cosmetic trinket", _ => "Cosmetic cape", }; } internal static bool Fits(Part part, ItemData item) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (item?.m_shared != null && item.m_shared.m_itemType == TypeOf(part)) { return !IsWorn(item); } return false; } internal static void Register() { Part[] parts = Parts; foreach (Part part in parts) { Part p = part; if (!API.AddSlot(SlotId(p), (Func<string>)(() => SlotName(p)), (Func<ItemData, bool>)((ItemData item) => Fits(p, item)), (Func<bool>)(() => Plugin.Enabled.Value))) { Plugin.Log.LogWarning((object)("ExtraSlots refused the slot " + SlotId(p) + ", check its free custom slots")); } } } internal static ItemData ItemIn(Part part) { try { return SlotItem(part); } catch { return null; } } internal static bool IsCosmeticItem(ItemData item) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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) if (item?.m_shared == null) { return false; } ItemType itemType = item.m_shared.m_itemType; Part[] parts = Parts; foreach (Part part in parts) { if (TypeOf(part) == itemType) { return ItemIn(part) == item; } } return false; } internal static List<ItemData> CosmeticItems() { return (from i in Parts.Select(ItemIn) where i != null select i).ToList(); } internal static Look GetLook(Part part) { Dictionary<string, string> dictionary = SavedData(); if (dictionary == null || !dictionary.TryGetValue("CosmeticArmor_Looks", out var value)) { return Look.Cosmetic; } if ((object)value != _parsedFrom) { string[] array = (value ?? "").Split(new char[1] { ',' }); for (int i = 0; i < Parsed.Length; i++) { Parsed[i] = ((i < array.Length && int.TryParse(array[i], out var result) && result >= 0 && result <= 2) ? ((Look)result) : Look.Cosmetic); } _parsedFrom = value; } return Parsed[(int)part]; } internal static void SetLook(Part part, Look look) { Dictionary<string, string> dictionary = SavedData(); if (dictionary != null) { Look[] array = Parts.Select(GetLook).ToArray(); array[(int)part] = look; dictionary["CosmeticArmor_Looks"] = string.Join(",", array.Select(delegate(Look l) { int num = (int)l; return num.ToString(); })); } } internal static Look Shown(Part part) { Look look = GetLook(part); if (look != Look.Cosmetic || ItemIn(part) != null) { return look; } return Look.Armor; } internal static Look Next(Look shown, bool hasItem) { switch (shown) { case Look.Cosmetic: return Look.Armor; case Look.Armor: return Look.Hidden; default: if (!hasItem) { return Look.Armor; } return Look.Cosmetic; } } internal static void Cycle(Part part) { Look look = Next(Shown(part), ItemIn(part) != null); SetLook(part, look); if (Plugin.LogLooks.Value) { Plugin.Log.LogInfo((object)$"{part} look set to {look}"); } Looks.Refresh(); } } internal static class CustomSlotItems { internal static Func<ItemData, bool> FitsOtherCustomSlot = delegate(ItemData item) { Slot[] customSlots = Slots.GetCustomSlots(true); foreach (Slot val in customSlots) { if (val != null && !Layout.IsOurs(val.ID) && val.ItemFits(item)) { return true; } } return false; }; private static bool _changedOnce; internal static void Patch(Harmony harmony) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method("ExtraSlots.Slots:IsCustomSlotItem", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.Log.LogWarning((object)"ExtraSlots.Slots.IsCustomSlotItem not found, extra utility slots may stay blocked by the cosmetic slots"); return; } harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(CustomSlotItems), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log.LogInfo((object)"custom slot check: the cosmetic slots no longer count, extra utility slots work with them"); } internal static void Postfix(ItemData item, ref bool __result) { if (!__result || item?.m_shared == null || FitsOtherCustomSlot(item)) { return; } __result = false; if (!_changedOnce) { _changedOnce = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("custom slot check: " + item.m_shared.m_name + " fits only a cosmetic slot, ExtraSlots treats it as normal equipment (first time this session)")); } } } } internal static class ExtraUtilityLooks { [HarmonyPatch(typeof(Humanoid), "SetupVisEquipment")] [HarmonyAfter(new string[] { "shudnal.ExtraSlots" })] private static class AfterSetup { private static void Postfix(Humanoid __instance, VisEquipment visEq) { if (!((Object)(object)visEq == (Object)null) && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Plugin.Enabled.Value && HideExtras()) { for (int i = 0; i < 4; i++) { Clear(visEq, i); } if (!_loggedOnce || Plugin.LogLooks.Value) { _loggedOnce = true; Plugin.Log.LogInfo((object)$"extra utility items hidden ({CosmeticSlots.Shown(Part.Utility)})"); } } } } private const int ExtraVisuals = 4; private static bool _loggedOnce; internal static Action<VisEquipment, int> Clear = delegate(VisEquipment vis, int index) { VisEquipmentExtension.SetCustomItemState(vis, index, ""); }; internal static bool HideExtras() { return CosmeticSlots.Shown(Part.Utility) != Look.Armor; } } internal static class Layout { internal struct SlotPos { public string Id; public Vector2 Pos; public bool Equipment; } internal const float Gap = 0.2f; internal const float TitleHeight = 20f; internal static bool GroupPlaced; private static float _widthDelta; private static float _heightDelta; private static string _lastLog; private static FieldRef<Slot, Vector2> _position; private const float DefaultPitch = 70f; private static FieldInfo _background; private static FieldInfo _selectedFrame; private static Vector2 _baseSize; private static Vector2 _basePos; private static bool _hasBase; internal static RectTransform Background { get { object? obj = _background?.GetValue(null); return (RectTransform)((obj is RectTransform) ? obj : null); } } internal static bool IsOurs(string id) { if (id != null) { if (!id.StartsWith("CosmeticArmor_")) { return id.StartsWith("CustomCosmeticArmor_"); } return true; } return false; } internal static float Pitch(List<SlotPos> equipment, float fallback) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_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) float num = float.MaxValue; for (int i = 0; i < equipment.Count; i++) { for (int j = i + 1; j < equipment.Count; j++) { Vector2 val = equipment[i].Pos - equipment[j].Pos; float num2 = Mathf.Abs(val.x); float num3 = Mathf.Abs(val.y); if (num2 < 0.5f && num3 > 0.5f) { num = Mathf.Min(num, num3); } if (num3 < 0.5f && num2 > 0.5f) { num = Mathf.Min(num, num2); } } } if (!(num < float.MaxValue)) { return fallback; } return num; } internal static Dictionary<string, Vector2> Plan(List<SlotPos> slots, float tile, out float widthDelta) { //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, Vector2> moves = new Dictionary<string, Vector2>(); widthDelta = 0f; List<SlotPos> list = slots.Where((SlotPos s) => s.Equipment).ToList(); List<SlotPos> list2 = list.Where((SlotPos s) => IsOurs(s.Id)).ToList(); if (list2.Count == 0 || tile <= 0f) { return moves; } List<SlotPos> list3 = list.Where((SlotPos s) => !IsOurs(s.Id)).ToList(); tile = Pitch(list, tile); float x0 = list.Min((SlotPos s) => s.Pos.x); if (list.Any((SlotPos s) => Mathf.Abs((s.Pos.x - x0) / tile - (float)Col(s.Pos)) > 0.1f)) { return moves; } int currentLast = list.Max((SlotPos s) => Col(s.Pos)); List<SlotPos> list4 = list3.Concat(slots.Where((SlotPos s) => !s.Equipment && Col(s.Pos) <= currentLast)).ToList(); int num = ((list4.Count > 0) ? list4.Max((SlotPos s) => Col(s.Pos)) : (-1)); int num2 = Math.Max(0, currentLast - num); float num3 = x0 + (float)num * tile; Vector2 val = default(Vector2); foreach (SlotPos item in slots.Where((SlotPos s) => !s.Equipment && Col(s.Pos) > currentLast)) { ((Vector2)(ref val))..ctor(item.Pos.x - (float)num2 * tile, item.Pos.y); moves[item.Id] = val; num3 = Mathf.Max(num3, val.x); } List<SlotPos> source = ((list3.Count > 0) ? list3 : list); List<float> list5 = (from s in source select s.Pos.y into y orderby y descending select y).ToList(); float top = list5[0]; float num4 = 0f; for (int num5 = 1; num5 < list5.Count; num5++) { float num6 = list5[num5 - 1] - list5[num5]; if (num6 > 1f && (num4 == 0f || num6 < num4)) { num4 = num6; } } float rowStep = ((num4 > 0f) ? (0f - num4) : (0f - tile)); int num7 = Math.Max(2, source.Select((SlotPos s) => Mathf.RoundToInt((s.Pos.y - top) / rowStep)).Distinct().Count()); int num8 = (list2.Count + num7 - 1) / num7; float num9 = num3 + tile * 1.2f; for (int num10 = 0; num10 < list2.Count; num10++) { moves[list2[num10].Id] = new Vector2(num9 + (float)(num10 / num7) * tile, top + (float)(num10 % num7) * rowStep); } Vector2 value; List<(int, int)> list6 = (from s in slots select (!moves.TryGetValue(s.Id, out value)) ? s.Pos : value into p select (Mathf.RoundToInt(p.x), Mathf.RoundToInt(p.y))).ToList(); if (list6.Distinct().Count() != list6.Count) { moves.Clear(); return moves; } widthDelta = tile * ((float)num8 + 0.2f - (float)num2); return moves; int Col(Vector2 p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Mathf.RoundToInt((p.x - x0) / tile); } } internal static void Grow(Vector2 size, Vector2 pos, Vector2 pivot, float width, float height, out Vector2 newSize, out Vector2 newPos) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_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) newSize = size + new Vector2(width, height); newPos = pos + new Vector2(width * pivot.x, height * pivot.y); } internal static void Patch(Harmony harmony) { //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown Type type = AccessTools.TypeByName("ExtraSlots.EquipmentPanel"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "SetSlotsPositions", (Type[])null, (Type[])null)); MethodInfo methodInfo2 = ((type == null) ? null : AccessTools.Method(type, "UpdateBackground", (Type[])null, (Type[])null)); _background = ((type == null) ? null : AccessTools.Field(type, "equipmentBackground")); _selectedFrame = ((type == null) ? null : AccessTools.Field(type, "selectedFrame")); if (AccessTools.Field(typeof(Slot), "_position")?.FieldType == typeof(Vector2)) { _position = AccessTools.FieldRefAccess<Slot, Vector2>("_position"); } if (methodInfo == null || methodInfo2 == null || _position == null || _background == null) { Plugin.Log.LogWarning((object)"this ExtraSlots version has another panel layout, the cosmetic slots stay in the armor grid"); return; } harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Layout), "AfterSlotsPositions", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(Layout), "AfterBackground", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static void AfterSlotsPositions() { //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) _widthDelta = 0f; _heightDelta = 0f; GroupPlaced = false; if (!Plugin.Enabled.Value || Plugin.UseExtraSlotsLayout.Value) { ApplyWidth(); return; } try { List<Slot> equipment = (from s in API.GetEquipmentSlots() where s != null && s.IsActive select s).ToList(); List<Slot> list = (from s in API.GetFoodSlots().Concat(API.GetAmmoSlots()).Concat(API.GetMiscSlots()) .Concat(API.GetQuickSlots()) where s != null && s.IsActive && !equipment.Contains(s) select s).Distinct().ToList(); List<Slot> list2 = equipment.Concat(list).ToList(); Dictionary<string, Vector2> dictionary = Plan(list2.Select((Slot s) => new SlotPos { Id = s.ID, Pos = s.Position, Equipment = equipment.Contains(s) }).ToList(), 70f, out _widthDelta); foreach (Slot item in list2) { if (dictionary.TryGetValue(item.ID, out var value)) { _position.Invoke(item) = value; } } GroupPlaced = dictionary.Keys.Any(IsOurs); _heightDelta = ((GroupPlaced && Plugin.ShowTitle.Value) ? 20f : 0f); string text = $"layout: {equipment.Count} armor slots, {list.Count} others, moved {dictionary.Count}, panel {_widthDelta:+0;-0;0} px wider, {_heightDelta:0} px taller"; if (Plugin.LogLooks.Value && text != _lastLog) { Plugin.Log.LogInfo((object)text); } _lastLog = text; ApplyWidth(); } catch (Exception ex) { _widthDelta = 0f; _heightDelta = 0f; GroupPlaced = false; ApplyWidth(); Plugin.Log.LogWarning((object)("could not place the cosmetic group: " + ex.Message)); } } private static void AfterBackground() { //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: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) object? value = _background.GetValue(null); RectTransform val = (RectTransform)((value is RectTransform) ? value : null); if (val != null && !((Object)(object)val == (Object)null)) { _baseSize = val.sizeDelta; _basePos = val.anchoredPosition; _hasBase = true; ApplyWidth(); } } private static void ApplyWidth() { //IL_0025: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) if (!_hasBase) { return; } object? value = _background.GetValue(null); RectTransform val = (RectTransform)((value is RectTransform) ? value : null); if (val != null && !((Object)(object)val == (Object)null)) { Grow(_baseSize, _basePos, val.pivot, _widthDelta, _heightDelta, out var newSize, out var newPos); val.sizeDelta = newSize; val.anchoredPosition = newPos; object? obj = _selectedFrame?.GetValue(null); RectTransform val2 = (RectTransform)((obj is RectTransform) ? obj : null); if (val2 != null && (Object)(object)val2 != (Object)null) { val2.sizeDelta = newSize + Vector2.one * 26f; val2.anchoredPosition = newPos; } } } } internal static class LookButtons { [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] [HarmonyAfter(new string[] { "shudnal.ExtraSlots" })] private static class AddButtons { private static void Postfix(InventoryGrid __instance) { if ((Object)(object)InventoryGui.instance == (Object)null || (Object)(object)__instance != (Object)(object)InventoryGui.instance.m_playerGrid) { return; } Title.Update(ElementsRef.Invoke(__instance)); if (_failed || !Plugin.Enabled.Value) { return; } try { Part[] parts = CosmeticSlots.Parts; foreach (Part part in parts) { UpdateButton(__instance, part); } } catch (Exception arg) { _failed = true; Plugin.Log.LogError((object)$"look buttons switched off: {arg}"); } } } private class Tag { internal InventoryElement Element; internal Transform Button; internal TMP_Text Text; internal Image Background; internal UITooltip Tooltip; internal Look Shown; } private const string ButtonName = "CosmeticArmor_Look"; private static readonly FieldRef<InventoryGrid, List<InventoryElement>> ElementsRef = AccessTools.FieldRefAccess<InventoryGrid, List<InventoryElement>>("m_elements"); private static bool _failed; private const float FadedIcon = 0.35f; private static readonly Tag[] Tags = new Tag[CosmeticSlots.Parts.Length]; internal static string Label(Look shown) { return shown switch { Look.Cosmetic => "Show", Look.Armor => "Armor", _ => "Hide", }; } internal static string Tooltip(Part part, Look shown) { return CosmeticSlots.NameOf(part) + ": " + shown switch { Look.Cosmetic => "shows the cosmetic item", Look.Armor => "shows your worn armor", _ => "shows nothing", } + ".\nClick to switch: Show (the cosmetic item), Armor (your worn armor), Hide (nothing)."; } internal static Color ColorOf(Look shown) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) return (Color)(shown switch { Look.Cosmetic => new Color(0.85f, 0.62f, 0.1f, 0.95f), Look.Armor => new Color(0.2f, 0.42f, 0.7f, 0.95f), _ => new Color(0.62f, 0.14f, 0.12f, 0.95f), }); } internal static InventoryElement ElementAt(List<InventoryElement> elements, Vector2i position) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) foreach (InventoryElement element in elements) { if ((Object)(object)element != (Object)null && element.Position == position) { return element; } } return null; } private static void UpdateButton(InventoryGrid grid, Part part) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_016f: 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_0188: Unknown result type (might be due to invalid IL or missing references) Slot val = API.FindSlot(CosmeticSlots.SlotId(part)); if (val == null || !val.IsActive) { return; } InventoryElement val2 = ElementAt(ElementsRef.Invoke(grid), val.GridPosition); if ((Object)(object)val2 == (Object)null) { return; } Tag tag = Tags[(int)part] ?? (Tags[(int)part] = new Tag()); if ((Object)(object)tag.Element != (Object)(object)val2 || (Object)(object)tag.Button == (Object)null) { Transform val3 = ((Component)val2).transform.Find("CosmeticArmor_Look"); if ((Object)(object)val3 == (Object)null) { val3 = Create(val2, part); } tag.Element = val2; tag.Button = val3; tag.Text = ((Component)val3).GetComponentInChildren<TMP_Text>(true); tag.Background = ((Component)val3).GetComponent<Image>(); tag.Tooltip = ((Component)val3).GetComponent<UITooltip>(); tag.Shown = (Look)(-1); } Look look = CosmeticSlots.Shown(part); if (look != tag.Shown) { tag.Shown = look; if ((Object)(object)tag.Text != (Object)null) { tag.Text.text = Label(look); } if ((Object)(object)tag.Background != (Object)null) { ((Graphic)tag.Background).color = ColorOf(look); } if ((Object)(object)tag.Tooltip != (Object)null) { tag.Tooltip.m_text = Tooltip(part, look); } } if ((Object)(object)val2.m_icon != (Object)null && ((Behaviour)val2.m_icon).enabled && look != Look.Cosmetic) { Color color = ((Graphic)val2.m_icon).color; color.a = 0.35f; ((Graphic)val2.m_icon).color = color; } tag.Button.SetAsLastSibling(); } private static Transform Create(InventoryElement element, Part part) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown //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_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012a: 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) //IL_0130: 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_0176: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("CosmeticArmor_Look", new Type[3] { typeof(RectTransform), typeof(Image), typeof(Button) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(((Component)element).transform, false); val2.anchorMin = new Vector2(0.06f, 0f); val2.anchorMax = new Vector2(0.94f, 0f); val2.pivot = new Vector2(0.5f, 0f); val2.anchoredPosition = new Vector2(0f, 2f); val2.sizeDelta = new Vector2(0f, 17f); ((UnityEvent)val.GetComponent<Button>().onClick).AddListener((UnityAction)delegate { CosmeticSlots.Cycle(part); }); GameObject val3 = new GameObject("Label", new Type[1] { typeof(RectTransform) }); RectTransform val4 = (RectTransform)val3.transform; ((Transform)val4).SetParent((Transform)(object)val2, false); val4.anchorMin = Vector2.zero; val4.anchorMax = Vector2.one; Vector2 offsetMin = (val4.offsetMax = Vector2.zero); val4.offsetMin = offsetMin; TextMeshProUGUI val5 = val3.AddComponent<TextMeshProUGUI>(); if ((Object)(object)element.m_quality != (Object)null) { ((TMP_Text)val5).font = element.m_quality.font; } ((TMP_Text)val5).fontSize = 13f; ((TMP_Text)val5).fontStyle = (FontStyles)1; ((Graphic)val5).color = Color.white; ((TMP_Text)val5).alignment = (TextAlignmentOptions)514; ((TMP_Text)val5).textWrappingMode = (TextWrappingModes)0; ((Graphic)val5).raycastTarget = false; if ((Object)(object)element.m_tooltip != (Object)null) { UITooltip obj = val.AddComponent<UITooltip>(); obj.m_tooltipPrefab = element.m_tooltip.m_tooltipPrefab; obj.m_topic = "Cosmetic Armor"; } val.transform.SetAsLastSibling(); return val.transform; } } internal static class Looks { [HarmonyPatch(typeof(VisEquipment), "SetHelmetItem")] private static class SetHelmetItem { private static void Prefix(VisEquipment __instance, ref int itemHash) { int variant = 0; int quality = 0; Apply(__instance, Part.Helmet, ref itemHash, ref variant, ref quality); } } [HarmonyPatch(typeof(VisEquipment), "SetChestItem")] private static class SetChestItem { private static void Prefix(VisEquipment __instance, ref int itemHash) { int variant = 0; int quality = 0; Apply(__instance, Part.Chest, ref itemHash, ref variant, ref quality); } } [HarmonyPatch(typeof(VisEquipment), "SetLegItem")] private static class SetLegItem { private static void Prefix(VisEquipment __instance, ref int itemHash) { int variant = 0; int quality = 0; Apply(__instance, Part.Legs, ref itemHash, ref variant, ref quality); } } [HarmonyPatch(typeof(VisEquipment), "SetShoulderItem")] private static class SetShoulderItem { private static void Prefix(VisEquipment __instance, ref int itemHash, ref int variant, ref int quality) { Apply(__instance, Part.Cape, ref itemHash, ref variant, ref quality); } } [HarmonyPatch(typeof(VisEquipment), "SetUtilityItem")] private static class SetUtilityItem { private static void Prefix(VisEquipment __instance, ref int itemHash) { int variant = 0; int quality = 0; Apply(__instance, Part.Utility, ref itemHash, ref variant, ref quality); } } [HarmonyPatch(typeof(VisEquipment), "SetTrinketItem")] private static class SetTrinketItem { private static void Prefix(VisEquipment __instance, ref string name) { if (Plugin.Enabled.Value && IsLocal(__instance)) { string text = name; name = ChooseName(CosmeticSlots.Shown(Part.Trinket), CosmeticSlots.ItemIn(Part.Trinket), name); if (Plugin.LogLooks.Value && name != text) { Plugin.Log.LogInfo((object)string.Format("Trinket shows {0} ({1})", string.IsNullOrEmpty(name) ? "nothing" : name, CosmeticSlots.Shown(Part.Trinket))); } } } } [HarmonyPatch(typeof(Player), "OnSpawned")] private static class WatchInventory { private static void Postfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { Inventory inventory = ((Humanoid)__instance).GetInventory(); inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, new Action(Refresh)); inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, new Action(Refresh)); Refresh(); } } } private static readonly FieldRef<Humanoid, VisEquipment> VisEquipmentRef = AccessTools.FieldRefAccess<Humanoid, VisEquipment>("m_visEquipment"); private static bool IsLocal(VisEquipment vis) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && (Object)(object)vis != (Object)null) { return (Object)(object)VisEquipmentRef.Invoke((Humanoid)(object)localPlayer) == (Object)(object)vis; } return false; } internal static int HashOf(ItemData item) { if (!((Object)(object)item?.m_dropPrefab == (Object)null)) { return StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name); } return 0; } internal static bool Choose(Look shown, ItemData cosmetic, ref int itemHash, ref int variant, ref int quality) { switch (shown) { case Look.Hidden: itemHash = 0; variant = 0; return true; case Look.Cosmetic: if (cosmetic != null) { int num = HashOf(cosmetic); if (num == 0) { return false; } itemHash = num; variant = cosmetic.m_variant; quality = cosmetic.m_quality; return true; } break; } return false; } private static void Apply(VisEquipment vis, Part part, ref int itemHash, ref int variant, ref int quality) { if (!Plugin.Enabled.Value || !IsLocal(vis)) { return; } int num = itemHash; Look look = CosmeticSlots.Shown(part); if (!Choose(look, CosmeticSlots.ItemIn(part), ref itemHash, ref variant, ref quality) || !Plugin.LogLooks.Value || itemHash == num) { return; } ManualLogSource log = Plugin.Log; object arg = part; object arg2; if (itemHash != 0) { ItemData obj = CosmeticSlots.ItemIn(part); if (obj == null) { arg2 = null; } else { GameObject dropPrefab = obj.m_dropPrefab; arg2 = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } } else { arg2 = "nothing"; } log.LogInfo((object)$"{arg} shows {arg2} ({look})"); } internal static void Refresh() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { VisEquipment val = VisEquipmentRef.Invoke((Humanoid)(object)localPlayer); if ((Object)(object)val != (Object)null) { Traverse.Create((object)localPlayer).Method("SetupVisEquipment", new Type[2] { typeof(VisEquipment), typeof(bool) }, (object[])null).GetValue(new object[2] { val, false }); } } } internal static string ChooseName(Look shown, ItemData cosmetic, string worn) { switch (shown) { case Look.Hidden: return ""; case Look.Cosmetic: if ((Object)(object)cosmetic?.m_dropPrefab != (Object)null) { return ((Object)cosmetic.m_dropPrefab).name; } break; } return worn; } } [BepInPlugin("onononoki.CosmeticArmor", "CosmeticArmor", "1.0.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInIncompatibility("Transmog")] [BepInIncompatibility("vanity")] [BepInIncompatibility("vktr.vanityoutfit")] [BepInIncompatibility("advize.Armoire")] public class Plugin : BaseUnityPlugin { public const string ModGuid = "onononoki.CosmeticArmor"; public const string ModName = "CosmeticArmor"; public const string ModVersion = "1.0.2"; internal const string ExtraSlotsGuid = "shudnal.ExtraSlots"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> ToGrave; internal static ConfigEntry<bool> LogLooks; internal static ConfigEntry<bool> UseExtraSlotsLayout; internal static ConfigEntry<bool> ShowTitle; private void Awake() { //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown //IL_014b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Cosmetic slots change how your armor looks. Off shows your worn armor again and hides the slots (their items stay safe in your inventory)."); ToGrave = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CosmeticItemsToGrave", false, "Off: cosmetic items stay with you when you die. On: they go to your grave like everything else (anything that does not fit stays with you)."); UseExtraSlotsLayout = ((BaseUnityPlugin)this).Config.Bind<bool>("Layout", "UseExtraSlotsLayout", false, "Fallback. Off: the cosmetic slots sit in their own group to the right of the food slots. On: ExtraSlots places them itself, in the armor grid with its other slots (use this if the group looks wrong after an ExtraSlots update)."); UseExtraSlotsLayout.SettingChanged += delegate { API.UpdateSlots(); }; ShowTitle = ((BaseUnityPlugin)this).Config.Bind<bool>("Layout", "ShowTitle", true, "Shows a \"Cosmetic\" title above the cosmetic group (the panel gets a little taller at the top). Off: no title, the panel keeps ExtraSlots' height."); ShowTitle.SettingChanged += delegate { API.UpdateSlots(); }; LogLooks = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogLooks", false, "Writes a log line whenever a cosmetic slot changes what your character shows."); Enabled.SettingChanged += delegate { API.UpdateSlots(); Looks.Refresh(); }; CosmeticSlots.Register(); Harmony val = new Harmony("onononoki.CosmeticArmor"); val.PatchAll(Assembly.GetExecutingAssembly()); Layout.Patch(val); CustomSlotItems.Patch(val); Log.LogInfo((object)"CosmeticArmor 1.0.2 loaded"); } } internal static class Title { private const string TitleName = "CosmeticArmor_Title"; internal const string Text = "Cosmetic"; internal const float Lift = 1f; internal const float BoxHeight = 18f; private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4]; private static bool _failed; internal static Vector2 Spot(float left, float right, float top) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return new Vector2((left + right) / 2f, top + 1f); } internal static void Update(List<InventoryElement> elements) { //IL_00ac: 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) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) if (_failed) { return; } try { RectTransform background = Layout.Background; if ((Object)(object)background == (Object)null) { return; } Transform val = ((Transform)background).Find("CosmeticArmor_Title"); if (!Plugin.Enabled.Value || !Plugin.ShowTitle.Value || !Layout.GroupPlaced || !GroupEdges(background, elements, out var left, out var right, out var top, out var font)) { if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(false); } return; } if ((Object)(object)val == (Object)null) { val = Create(background, font); } if (!((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } RectTransform val2 = (RectTransform)val; Vector2 anchorMin = (val2.anchorMax = background.pivot); val2.anchorMin = anchorMin; val2.sizeDelta = new Vector2(right - left, 18f); val2.anchoredPosition = Spot(left, right, top); val.SetAsLastSibling(); } catch (Exception arg) { _failed = true; Plugin.Log.LogError((object)$"cosmetic title switched off: {arg}"); } } private static bool GroupEdges(RectTransform panel, List<InventoryElement> elements, out float left, out float right, out float top, out TMP_FontAsset font) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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_00a4: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) left = float.MaxValue; right = float.MinValue; top = float.MinValue; font = null; bool result = false; Part[] parts = CosmeticSlots.Parts; for (int i = 0; i < parts.Length; i++) { Slot val = API.FindSlot(CosmeticSlots.SlotId(parts[i])); if (val == null || !val.IsActive) { continue; } InventoryElement val2 = LookButtons.ElementAt(elements, val.GridPosition); if (!((Object)(object)val2 == (Object)null) && ((Component)val2).gameObject.activeInHierarchy) { ((RectTransform)((Component)val2).transform).GetWorldCorners(Corners); Vector3[] corners = Corners; foreach (Vector3 val3 in corners) { Vector3 val4 = ((Transform)panel).InverseTransformPoint(val3); left = Mathf.Min(left, val4.x); right = Mathf.Max(right, val4.x); top = Mathf.Max(top, val4.y); } if ((Object)(object)font == (Object)null && (Object)(object)val2.m_quality != (Object)null) { font = val2.m_quality.font; } result = true; } } return result; } private static Transform Create(RectTransform panel, TMP_FontAsset font) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_003a: 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_008d: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("CosmeticArmor_Title", new Type[1] { typeof(RectTransform) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent((Transform)(object)panel, false); val2.pivot = new Vector2(0.5f, 0f); TextMeshProUGUI val3 = val.AddComponent<TextMeshProUGUI>(); if ((Object)(object)font != (Object)null) { ((TMP_Text)val3).font = font; } ((TMP_Text)val3).text = "Cosmetic"; ((TMP_Text)val3).fontSize = 16f; ((TMP_Text)val3).fontStyle = (FontStyles)1; ((Graphic)val3).color = new Color(0.96f, 0.8f, 0.45f, 1f); ((TMP_Text)val3).alignment = (TextAlignmentOptions)1026; ((TMP_Text)val3).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)val3).overflowMode = (TextOverflowModes)0; ((Graphic)val3).raycastTarget = false; return val.transform; } } internal static class WeightAndDeath { [HarmonyPatch(typeof(ItemData), "GetWeight")] [HarmonyAfter(new string[] { "shudnal.ExtraSlots" })] [HarmonyPriority(0)] private static class NoWeight { private static void Postfix(ItemData __instance, ref float __result) { if (__result != 0f && CosmeticSlots.IsCosmeticItem(__instance)) { __result = 0f; } } } [HarmonyPatch(typeof(Inventory), "MoveInventoryToGrave")] [HarmonyPriority(800)] private static class KeepOnDeath { private static void Prefix(Inventory original, ref List<ItemData> __state) { if (original == null || original != PlayerInventory()) { return; } List<ItemData> cosmetic = CosmeticSlots.CosmeticItems(); __state = cosmetic; if (cosmetic.Count != 0 && !Plugin.ToGrave.Value) { ItemsRef.Invoke(original).RemoveAll((ItemData i) => cosmetic.Contains(i)); } } private static void Postfix(Inventory __instance, Inventory original, List<ItemData> __state) { if (__state == null || __state.Count == 0) { return; } List<ItemData> list = ItemsRef.Invoke(original); int num = 0; foreach (ItemData item in __state) { if (!list.Contains(item) && (!Plugin.ToGrave.Value || !__instance.ContainsItem(item))) { list.Add(item); num++; } } if (num > 0) { Changed(original); } Plugin.Log.LogInfo((object)(Plugin.ToGrave.Value ? $"death: {__state.Count - num} cosmetic item(s) went to the grave, {num} stayed with you" : $"death: {num} cosmetic item(s) stayed with you")); } } internal static Func<Inventory> PlayerInventory = delegate { Player localPlayer = Player.m_localPlayer; return (localPlayer == null) ? null : ((Humanoid)localPlayer).GetInventory(); }; private static readonly FieldRef<Inventory, List<ItemData>> ItemsRef = AccessTools.FieldRefAccess<Inventory, List<ItemData>>("m_inventory"); private static readonly Action<Inventory> Changed = delegate(Inventory inventory) { Traverse.Create((object)inventory).Method("Changed", new Type[2] { typeof(bool), typeof(bool) }, (object[])null).GetValue(new object[2] { false, false }); }; } }