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 Enable; internal static ConfigEntry HostOnly; internal static ConfigEntry HostOnlyClientCompatibleMode; internal static ConfigEntry ShowDebugLog; internal static ConfigEntry AccelerateTokenInsert; internal static ConfigEntry FreezeResultScreenSpin; internal static ConfigEntry MinimumStateSeconds; internal static ConfigEntry TokenInsertSeconds; internal static ConfigEntry ResultScreenSeconds; internal static ConfigEntry 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("General", "Enable", true, "Enable instant cosmetic gacha results."); HostOnly = ((BaseUnityPlugin)this).Config.Bind("Network", "HostOnly", true, "Only the master client accelerates the authoritative machine state."); HostOnlyClientCompatibleMode = ((BaseUnityPlugin)this).Config.Bind("Network", "HostOnlyClientCompatibleMode", true, "Keep reward display states long enough for clients without this mod to see the result."); ShowDebugLog = ((BaseUnityPlugin)this).Config.Bind("General", "ShowDebugLog", false, "Write detailed patch logs."); AccelerateTokenInsert = ((BaseUnityPlugin)this).Config.Bind("Speed", "AccelerateTokenInsert", true, "Shorten the button press to token insertion phase."); FreezeResultScreenSpin = ((BaseUnityPlugin)this).Config.Bind("Screen", "FreezeResultScreenSpin", true, "Stop the machine screen from spinning while the reward result is displayed."); MinimumStateSeconds = ((BaseUnityPlugin)this).Config.Bind("Speed", "MinimumStateSeconds", 0.1f, "Small delay used so reward fetch and screen update states still run."); TokenInsertSeconds = ((BaseUnityPlugin)this).Config.Bind("Speed", "TokenInsertSeconds", 0.08f, "How long to wait before marking the token insertion animation as complete."); ResultScreenSeconds = ((BaseUnityPlugin)this).Config.Bind("Screen", "ResultScreenSeconds", 1.5f, "How long the machine result display stays visible."); ClientCompatibleResultSeconds = ((BaseUnityPlugin)this).Config.Bind("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 Timers = new ConditionalWeakTable(); 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()); 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); } } }