Decompiled source of QuickGachaMachine v1.0.0

QuickGachaMachine.dll

Decompiled 2 days ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace QuickGachaMachine
{
	[BepInPlugin("jiu.repo.quickgachamachine", "快速扭蛋机", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "jiu.repo.quickgachamachine";

		public const string PluginName = "快速扭蛋机";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enable;

		internal static ConfigEntry<bool> HostOnly;

		internal static ConfigEntry<bool> HostOnlyClientCompatibleMode;

		internal static ConfigEntry<bool> ShowDebugLog;

		internal static ConfigEntry<bool> AccelerateTokenInsert;

		internal static ConfigEntry<bool> FreezeResultScreenSpin;

		internal static ConfigEntry<float> MinimumStateSeconds;

		internal static ConfigEntry<float> TokenInsertSeconds;

		internal static ConfigEntry<float> ResultScreenSeconds;

		internal static ConfigEntry<float> ClientCompatibleResultSeconds;

		private Harmony harmony;

		private void Awake()
		{
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable", true, "Enable instant cosmetic gacha results.");
			HostOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Network", "HostOnly", true, "Only the master client accelerates the authoritative machine state.");
			HostOnlyClientCompatibleMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Network", "HostOnlyClientCompatibleMode", true, "Keep reward display states long enough for clients without this mod to see the result.");
			ShowDebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowDebugLog", false, "Write detailed patch logs.");
			AccelerateTokenInsert = ((BaseUnityPlugin)this).Config.Bind<bool>("Speed", "AccelerateTokenInsert", true, "Shorten the button press to token insertion phase.");
			FreezeResultScreenSpin = ((BaseUnityPlugin)this).Config.Bind<bool>("Screen", "FreezeResultScreenSpin", true, "Stop the machine screen from spinning while the reward result is displayed.");
			MinimumStateSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "MinimumStateSeconds", 0.1f, "Small delay used so reward fetch and screen update states still run.");
			TokenInsertSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "TokenInsertSeconds", 0.08f, "How long to wait before marking the token insertion animation as complete.");
			ResultScreenSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Screen", "ResultScreenSeconds", 1.5f, "How long the machine result display stays visible.");
			ClientCompatibleResultSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Screen", "ClientCompatibleResultSeconds", 2.5f, "Host-side result display time when clients may not have this mod installed.");
			harmony = new Harmony("jiu.repo.quickgachamachine");
			harmony.PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)"快速扭蛋机 loaded. Host install accelerates the room gacha machine while clients still see the machine screen result.");
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}

		internal static bool ShouldAccelerateAuthority()
		{
			if (!Enable.Value)
			{
				return false;
			}
			if (HostOnly.Value && PhotonNetwork.InRoom)
			{
				return PhotonNetwork.IsMasterClient;
			}
			return true;
		}

		internal static void DebugLog(string message)
		{
			if (ShowDebugLog.Value)
			{
				Log.LogInfo((object)message);
			}
		}
	}
	[HarmonyPatch(typeof(CosmeticShopMachine), "StateMachine")]
	internal static class CosmeticShopMachineStateMachinePatch
	{
		private static readonly FieldInfo StateCurrentField = AccessTools.Field(typeof(CosmeticShopMachine), "stateCurrent");

		private static readonly FieldInfo StateTimerField = AccessTools.Field(typeof(CosmeticShopMachine), "stateTimer");

		private static readonly FieldInfo HideTokenUITimerField = AccessTools.Field(typeof(CosmeticShopMachine), "hideTokenUITimer");

		private static readonly FieldInfo ScreenSpinStoppedField = AccessTools.Field(typeof(CosmeticShopMachine), "screenSpinStopped");

		private static readonly FieldInfo ScreenSpinStartedField = AccessTools.Field(typeof(CosmeticShopMachine), "screenSpinStarted");

		private static readonly FieldInfo ScreenSpinningFastField = AccessTools.Field(typeof(CosmeticShopMachine), "screenSpinningFast");

		private static readonly FieldInfo ScreenSpinSpeedField = AccessTools.Field(typeof(CosmeticShopMachine), "screenSpinSpeed");

		private static readonly FieldInfo ScreenSpinTimeField = AccessTools.Field(typeof(CosmeticShopMachine), "screenSpinTime");

		private static readonly FieldInfo ScreenSpinSettleLerpField = AccessTools.Field(typeof(CosmeticShopMachine), "screenSpinSettleLerp");

		private static readonly int Interact = StateValue("Interact");

		private static readonly int FetchReward = StateValue("FetchReward");

		private static readonly int FetchRewardDone = StateValue("FetchRewardDone");

		private static readonly int TokenOutro = StateValue("TokenOutro");

		private static readonly int RewardCurrencyIntro = StateValue("RewardCurrencyIntro");

		private static readonly int RewardCurrency = StateValue("RewardCurrency");

		private static readonly int RewardCurrencyOutro = StateValue("RewardCurrencyOutro");

		private static readonly int RewardCosmeticIntro = StateValue("RewardCosmeticIntro");

		private static readonly int RewardCosmeticConfetti = StateValue("RewardCosmeticConfetti");

		private static readonly int RewardCosmeticFrame = StateValue("RewardCosmeticFrame");

		private static readonly int RewardCosmeticFrameOutro = StateValue("RewardCosmeticFrameOutro");

		private static readonly int RewardCosmeticOutro = StateValue("RewardCosmeticOutro");

		private static void Postfix(CosmeticShopMachine __instance)
		{
			if (!Plugin.Enable.Value)
			{
				return;
			}
			int state = Convert.ToInt32(StateCurrentField.GetValue(__instance));
			if (!IsAcceleratedState(state))
			{
				return;
			}
			if (Plugin.ShouldAccelerateAuthority())
			{
				float num = Mathf.Max(0.01f, Plugin.MinimumStateSeconds.Value);
				float num2 = (IsResultDisplayState(state) ? GetResultDelay(state) : num);
				if ((float)StateTimerField.GetValue(__instance) > num2)
				{
					StateTimerField.SetValue(__instance, num2);
				}
				HideTokenUITimerField.SetValue(__instance, 0f);
				Plugin.DebugLog("Accelerated CosmeticShopMachine state " + state);
			}
			if (Plugin.FreezeResultScreenSpin.Value && IsResultDisplayState(state))
			{
				ScreenSpinStoppedField.SetValue(__instance, true);
				ScreenSpinStartedField.SetValue(__instance, false);
				ScreenSpinningFastField.SetValue(__instance, false);
				ScreenSpinSpeedField.SetValue(__instance, 0f);
				ScreenSpinTimeField.SetValue(__instance, 0f);
				ScreenSpinSettleLerpField.SetValue(__instance, 1f);
			}
		}

		private static bool IsAcceleratedState(int state)
		{
			if (state != Interact && state != FetchReward && state != FetchRewardDone && state != TokenOutro && state != RewardCurrencyIntro && state != RewardCurrency && state != RewardCurrencyOutro && state != RewardCosmeticIntro && state != RewardCosmeticConfetti && state != RewardCosmeticFrame && state != RewardCosmeticFrameOutro)
			{
				return state == RewardCosmeticOutro;
			}
			return true;
		}

		private static bool IsResultDisplayState(int state)
		{
			if (state != RewardCurrency && state != RewardCurrencyOutro && state != RewardCosmeticConfetti && state != RewardCosmeticFrame && state != RewardCosmeticFrameOutro)
			{
				return state == RewardCosmeticOutro;
			}
			return true;
		}

		private static float GetResultDelay(int state)
		{
			float num = Mathf.Max(0.05f, Plugin.ResultScreenSeconds.Value);
			if (!Plugin.HostOnlyClientCompatibleMode.Value || !PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient)
			{
				return num;
			}
			if (state == RewardCurrency || state == RewardCosmeticFrame)
			{
				return Mathf.Max(num, Plugin.ClientCompatibleResultSeconds.Value);
			}
			return Mathf.Max(num, Plugin.MinimumStateSeconds.Value);
		}

		private static int StateValue(string name)
		{
			return (int)Enum.Parse(AccessTools.Inner(typeof(CosmeticShopMachine), "State"), name);
		}
	}
	[HarmonyPatch(typeof(CosmeticShopMachineAnimator), "TokenLogic")]
	internal static class CosmeticShopMachineAnimatorTokenPatch
	{
		private sealed class TokenStopwatch
		{
			public readonly float StartedAt;

			public TokenStopwatch(float startedAt)
			{
				StartedAt = startedAt;
			}
		}

		private static readonly FieldInfo ControllerField = AccessTools.Field(typeof(CosmeticShopMachineAnimator), "controller");

		private static readonly FieldInfo TokenIntroDoneField = AccessTools.Field(typeof(CosmeticShopMachineAnimator), "tokenIntroDone");

		private static readonly FieldInfo TokenMeshFollowLerpField = AccessTools.Field(typeof(CosmeticShopMachineAnimator), "tokenMeshFollowLerp");

		private static readonly FieldInfo StateCurrentField = AccessTools.Field(typeof(CosmeticShopMachine), "stateCurrent");

		private static readonly MethodInfo EventTokenIntroDoneMethod = AccessTools.Method(typeof(CosmeticShopMachineAnimator), "EventTokenIntroDone", (Type[])null, (Type[])null);

		private static readonly int Interact = StateValue("Interact");

		private static readonly int FetchRewardDone = StateValue("FetchRewardDone");

		private static readonly ConditionalWeakTable<CosmeticShopMachineAnimator, TokenStopwatch> Timers = new ConditionalWeakTable<CosmeticShopMachineAnimator, TokenStopwatch>();

		private static void Postfix(CosmeticShopMachineAnimator __instance)
		{
			if (!Plugin.ShouldAccelerateAuthority() || !Plugin.AccelerateTokenInsert.Value || (bool)TokenIntroDoneField.GetValue(__instance))
			{
				Timers.Remove(__instance);
				return;
			}
			object? value = ControllerField.GetValue(__instance);
			CosmeticShopMachine val = (CosmeticShopMachine)((value is CosmeticShopMachine) ? value : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int num = Convert.ToInt32(StateCurrentField.GetValue(val));
			if (num != Interact && num != FetchRewardDone)
			{
				Timers.Remove(__instance);
				return;
			}
			TokenStopwatch value2 = Timers.GetValue(__instance, (CosmeticShopMachineAnimator _) => new TokenStopwatch(Time.unscaledTime));
			if (!(Time.unscaledTime - value2.StartedAt < Mathf.Max(0.01f, Plugin.TokenInsertSeconds.Value)))
			{
				TokenMeshFollowLerpField.SetValue(__instance, 1f);
				EventTokenIntroDoneMethod.Invoke(__instance, Array.Empty<object>());
				Timers.Remove(__instance);
				Plugin.DebugLog("Token insertion animation completed early.");
			}
		}

		private static int StateValue(string name)
		{
			return (int)Enum.Parse(AccessTools.Inner(typeof(CosmeticShopMachine), "State"), name);
		}
	}
}