Decompiled source of Duckify v0.1.0

plugins/Duckify.Core.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("Duckify.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d6c8c358d71e57c629525ea9f368fd92dc9e9aa1")]
[assembly: AssemblyProduct("Duckify.Core")]
[assembly: AssemblyTitle("Duckify.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Duckify.Core
{
	public static class MascotSuit
	{
		public const string PiecePrefix = "Mascot_";

		public const string Empty = "None";

		public static int? Pick(IReadOnlyList<string?> itemNames)
		{
			int? result = null;
			for (int i = 0; i < itemNames.Count; i++)
			{
				if (IsPiece(itemNames[i]))
				{
					return i;
				}
				if (!result.HasValue && itemNames[i] == "None")
				{
					result = i;
				}
			}
			return result;
		}

		public static int? EmptyIndex(IReadOnlyList<string?> itemNames)
		{
			for (int i = 0; i < itemNames.Count; i++)
			{
				if (itemNames[i] == "None")
				{
					return i;
				}
			}
			return null;
		}

		public static bool IsPiece(string? itemName)
		{
			return itemName?.StartsWith("Mascot_", StringComparison.Ordinal) ?? false;
		}
	}
	public readonly record struct Pick(int Slot, int Selection, int Material);
	public sealed class Wardrobe
	{
		private readonly Dictionary<long, SortedDictionary<int, Pick>> _outfits = new Dictionary<long, SortedDictionary<int, Pick>>();

		public int Count => _outfits.Count;

		public void Record(long character, int slot, int selection, int material)
		{
			if (!_outfits.TryGetValue(character, out SortedDictionary<int, Pick> value))
			{
				value = (_outfits[character] = new SortedDictionary<int, Pick>());
			}
			value[slot] = new Pick(slot, selection, material);
		}

		public bool Has(long character, int slot)
		{
			if (_outfits.TryGetValue(character, out SortedDictionary<int, Pick> value))
			{
				return value.ContainsKey(slot);
			}
			return false;
		}

		public IReadOnlyList<Pick> Outfit(long character)
		{
			if (!_outfits.TryGetValue(character, out SortedDictionary<int, Pick> value))
			{
				return Array.Empty<Pick>();
			}
			return value.Values.ToArray();
		}

		public void KeepOnly(IEnumerable<long> characters)
		{
			HashSet<long> live = new HashSet<long>(characters);
			foreach (long item in _outfits.Keys.Where((long k) => !live.Contains(k)).ToList())
			{
				_outfits.Remove(item);
			}
		}
	}
}

plugins/Duckify.Plugin.dll

Decompiled 8 hours ago
using 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.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CC;
using CayplayMissions.CustomizationNew;
using Duckify.Core;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("Duckify.Plugin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+d6c8c358d71e57c629525ea9f368fd92dc9e9aa1")]
[assembly: AssemblyProduct("Duckify.Plugin")]
[assembly: AssemblyTitle("Duckify.Plugin")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Duckify.Plugin
{
	[BepInPlugin("com.github.kirby1997.duckify", "Duckify", "0.1.0")]
	public sealed class Plugin : BasePlugin
	{
		public const string Id = "com.github.kirby1997.duckify";

		public const string Name = "Duckify";

		public const string Version = "0.1.0";

		private readonly HashSet<string> _reported = new HashSet<string>();

		internal static Plugin Instance { get; private set; }

		internal ConfigEntry<bool> Enabled { get; private set; }

		public override void Load()
		{
			//IL_0043: 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_0067: Expected O, but got Unknown
			Instance = this;
			Enabled = ((BasePlugin)this).Config.Bind<bool>("General", "Enabled", true, "Dress every visitor in the park mascot's duck suit. Turn off to give them their own clothes back; the change shows at once.");
			Enabled.SettingChanged += OnEnabledChanged;
			new Harmony("com.github.kirby1997.duckify").PatchAll(typeof(SuitHooks));
			ManualLogSource log = ((BasePlugin)this).Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(8, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("Duckify");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("0.1.0");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded");
			}
			log.LogInfo(val);
		}

		private void OnEnabledChanged(object sender, EventArgs args)
		{
			try
			{
				SuitHooks.RedressVisitors();
			}
			catch (Exception e)
			{
				ReportOnce("redressing visitors", e);
			}
		}

		internal void ReportOnce(string what, Exception e)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			if (_reported.Add(what))
			{
				ManualLogSource log = ((BasePlugin)this).Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(9, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(what);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(e);
				}
				log.LogError(val);
			}
		}
	}
	[HarmonyPatch]
	internal static class SuitHooks
	{
		private readonly struct Table
		{
			public readonly int? Piece;

			public readonly int? Empty;

			public Table(int? piece, int? empty)
			{
				Piece = piece;
				Empty = empty;
			}
		}

		internal struct Swap
		{
			public bool Done;

			public string Original;

			public byte OriginalMaterial;

			public string Piece;

			public string[] Names;

			public byte[] Materials;
		}

		private static readonly Dictionary<IntPtr, Table> Tables = new Dictionary<IntPtr, Table>();

		private static readonly Wardrobe Wardrobe = new Wardrobe();

		private static int _purgeAt = 512;

		private static int _swapsUnderway;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(CharacterCustomization), "setApparel")]
		private static void BeforeSetApparel(CharacterCustomization __instance, ref int selection, int slot, ref int materialSelection, out Swap __state)
		{
			__state = default(Swap);
			try
			{
				if ((Object)(object)((Component)__instance).GetComponent<VisitorsOutfitOverride>() == (Object)null)
				{
					return;
				}
				bool flag = _swapsUnderway > 0;
				if (!flag)
				{
					RecordPick(__instance, slot, selection, materialSelection);
				}
				if (!Plugin.Instance.Enabled.Value)
				{
					return;
				}
				List<scrObj_Apparel> apparelTables = __instance.ApparelTables;
				if (apparelTables == null || slot < 0 || slot >= apparelTables.Count)
				{
					return;
				}
				scrObj_Apparel val = apparelTables[slot];
				List<Apparel> val2 = ((val != null) ? val.Items : null);
				if (val2 == null || selection < 0 || selection >= val2.Count)
				{
					return;
				}
				int? piece = Describe(val).Piece;
				if (piece.HasValue && piece != selection)
				{
					Swap swap = new Swap
					{
						Done = true,
						Original = val2[selection].Name,
						OriginalMaterial = (byte)materialSelection,
						Piece = val2[piece.Value].Name
					};
					if (!flag)
					{
						CC_CharacterData storedCharacterData = __instance.StoredCharacterData;
						swap.Names = Copy((storedCharacterData != null) ? storedCharacterData.ApparelNames : null);
						swap.Materials = Copy((storedCharacterData != null) ? storedCharacterData.ApparelMaterials : null);
					}
					__state = swap;
					_swapsUnderway++;
					selection = piece.Value;
					materialSelection = 0;
				}
			}
			catch (Exception e)
			{
				__state = default(Swap);
				Plugin.Instance.ReportOnce("suit swap", e);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(CharacterCustomization), "setApparel")]
		private static void AfterSetApparel(CharacterCustomization __instance, int slot, Swap __state)
		{
			if (!__state.Done)
			{
				return;
			}
			_swapsUnderway = Math.Max(0, _swapsUnderway - 1);
			if (__state.Names == null)
			{
				return;
			}
			try
			{
				CC_CharacterData storedCharacterData = __instance.StoredCharacterData;
				List<string> val = ((storedCharacterData != null) ? storedCharacterData.ApparelNames : null);
				if (val != null)
				{
					for (int i = 0; i < val.Count; i++)
					{
						string text = ((i != slot) ? ((i < __state.Names.Length) ? __state.Names[i] : val[i]) : ((val[i] == __state.Piece) ? __state.Original : val[i]));
						if (val[i] != text)
						{
							val[i] = text;
						}
					}
				}
				List<byte> val2 = ((storedCharacterData != null) ? storedCharacterData.ApparelMaterials : null);
				if (val2 == null)
				{
					return;
				}
				for (int j = 0; j < val2.Count; j++)
				{
					byte b = ((j == slot) ? __state.OriginalMaterial : ((j < __state.Materials?.Length) ? __state.Materials[j] : val2[j]));
					if (val2[j] != b)
					{
						val2[j] = b;
					}
				}
			}
			catch (Exception e)
			{
				Plugin.Instance.ReportOnce("restoring a visitor's own outfit", e);
			}
		}

		internal static void RedressVisitors()
		{
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Expected O, but got Unknown
			//IL_00da: 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)
			List<long> list = new List<long>();
			foreach (VisitorsOutfitOverride item in Object.FindObjectsByType<VisitorsOutfitOverride>((FindObjectsInactive)1, (FindObjectsSortMode)0))
			{
				try
				{
					CharacterCustomization component = ((Component)item).GetComponent<CharacterCustomization>();
					if ((Object)(object)component == (Object)null)
					{
						continue;
					}
					long num = ((Il2CppObjectBase)component).Pointer.ToInt64();
					list.Add(num);
					IReadOnlyList<Pick> readOnlyList = Wardrobe.Outfit(num);
					List<scrObj_Apparel> apparelTables = component.ApparelTables;
					int num2 = 0;
					while (apparelTables != null && num2 < apparelTables.Count)
					{
						if (!Wardrobe.Has(num, num2) && !((Object)(object)apparelTables[num2] == (Object)null))
						{
							int? empty = Describe(apparelTables[num2]).Empty;
							if (empty.HasValue)
							{
								component.setApparel(empty.Value, num2, 0, true);
							}
						}
						num2++;
					}
					foreach (Pick item2 in readOnlyList)
					{
						Pick current2 = item2;
						component.setApparel(((Pick)(ref current2)).Selection, ((Pick)(ref current2)).Slot, ((Pick)(ref current2)).Material, true);
					}
				}
				catch (Exception e)
				{
					Plugin.Instance.ReportOnce("redressing a visitor", e);
				}
			}
			Wardrobe.KeepOnly((IEnumerable<long>)list);
			ManualLogSource log = ((BasePlugin)Plugin.Instance).Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(20, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Redressed ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(list.Count);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" visitors.");
			}
			log.LogInfo(val);
		}

		private static void RecordPick(CharacterCustomization character, int slot, int selection, int material)
		{
			Wardrobe.Record(((Il2CppObjectBase)character).Pointer.ToInt64(), slot, selection, material);
			if (Wardrobe.Count >= _purgeAt)
			{
				IEnumerable<long> enumerable = (from v in (IEnumerable<VisitorsOutfitOverride>)Object.FindObjectsByType<VisitorsOutfitOverride>((FindObjectsInactive)1, (FindObjectsSortMode)0)
					select ((Component)v).GetComponent<CharacterCustomization>() into c
					where (Object)(object)c != (Object)null
					select ((Il2CppObjectBase)c).Pointer.ToInt64()).Append(((Il2CppObjectBase)character).Pointer.ToInt64());
				Wardrobe.KeepOnly(enumerable);
				_purgeAt = Math.Max(512, Wardrobe.Count * 2);
			}
		}

		private static Table Describe(scrObj_Apparel table)
		{
			if (Tables.TryGetValue(((Il2CppObjectBase)table).Pointer, out var value))
			{
				return value;
			}
			List<Apparel> items = table.Items;
			string[] array = new string[items?.Count ?? 0];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = items[i].Name;
			}
			return Tables[((Il2CppObjectBase)table).Pointer] = new Table(MascotSuit.Pick((IReadOnlyList<string>)array), MascotSuit.EmptyIndex((IReadOnlyList<string>)array));
		}

		private static string[] Copy(List<string> list)
		{
			if (list == null)
			{
				return null;
			}
			string[] array = new string[list.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = list[i];
			}
			return array;
		}

		private static byte[] Copy(List<byte> list)
		{
			if (list == null)
			{
				return null;
			}
			byte[] array = new byte[list.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = list[i];
			}
			return array;
		}
	}
}