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 QuickGachaMachine v1.0.0
QuickGachaMachine.dll
Decompiled 2 days agousing 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); } } }