using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("REPOJP")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("zabuMod")] [assembly: AssemblyTitle("zabuMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 REPOJP.FastCosmeticGacha { [BepInPlugin("REPOJP.FastCosmeticGacha", "FastCosmeticGacha", "1.1.0")] public class FastCosmeticGachaPlugin : BaseUnityPlugin { public const string PluginGuid = "REPOJP.FastCosmeticGacha"; public const string PluginName = "FastCosmeticGacha"; public const string PluginVersion = "1.1.0"; internal static ManualLogSource Log; private Harmony harmony; internal static ConfigEntry Enable; internal static ConfigEntry ShowDebugLog; internal static ConfigEntry SpeedMode; internal static ConfigEntry AnimatorSpeedMultiplier; internal static ConfigEntry StateTimerMultiplier; internal static ConfigEntry SkipTokenOutro; internal static ConfigEntry SkipSpinWait; internal static ConfigEntry SkipSEWait; internal static ConfigEntry SkipConfettiWait; internal static ConfigEntry InstantResult; internal static ConfigEntry CosmeticResultDisplaySeconds; internal static ConfigEntry CurrencyResultDisplaySeconds; internal static ConfigEntry ReturnToIdleSeconds; internal static ConfigEntry AccelerateAuthoritativeStateWhenHost; internal static ConfigEntry MinimumStateSeconds; internal static ConfigEntry ResetAnimatorSpeedOnIdle; internal static ConfigEntry DoNotSkipRewardSetState; internal static ConfigEntry TurboMode; internal static ConfigEntry AllowQueuedMultiUse; internal static ConfigEntry QueueLocalPresses; internal static ConfigEntry QueueRemoteRequests; internal static ConfigEntry MaxQueueSize; internal static ConfigEntry MaxQueuedRequestsPerPlayer; internal static ConfigEntry QueueProcessDelaySeconds; internal static ConfigEntry QueueInteractDistance; internal static ConfigEntry AllowRapidPress; internal static ConfigEntry RapidPressCooldownSeconds; private void Awake() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown try { Log = ((BaseUnityPlugin)this).Logger; ((Component)this).transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); BindConfig(); GameReflection.Initialize(); harmony = new Harmony("REPOJP.FastCosmeticGacha"); PatchGameMethod("CosmeticShopMachine", "Update", typeof(MachineUpdateHook).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic), typeof(MachineUpdateHook).GetMethod("Postfix", BindingFlags.Static | BindingFlags.NonPublic)); PatchGameMethod("CosmeticShopMachine", "UpdateStateRPC", null, typeof(StateRpcHook).GetMethod("Postfix", BindingFlags.Static | BindingFlags.NonPublic)); PatchGameMethod("CosmeticShopMachine", "ScreenSpinLogic", null, typeof(ScreenSpinHook).GetMethod("Postfix", BindingFlags.Static | BindingFlags.NonPublic)); PatchGameMethod("CosmeticShopMachine", "InteractClientRPC", typeof(InteractClientRpcHook).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic), null); PatchGameMethod("CosmeticShopMachine", "RewardSet", null, typeof(RewardSetHook).GetMethod("Postfix", BindingFlags.Static | BindingFlags.NonPublic)); PatchGameMethod("CosmeticShopMachineAnimator", "Update", null, typeof(AnimatorUpdateHook).GetMethod("Postfix", BindingFlags.Static | BindingFlags.NonPublic)); PatchGameMethod("CosmeticShopMachineConfetti", "Set", null, typeof(ConfettiSetHook).GetMethod("Postfix", BindingFlags.Static | BindingFlags.NonPublic)); WriteDebugLog("Loaded"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failure: Awake\n" + ex)); } } private void OnDestroy() { try { if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failure: OnDestroy\n" + ex)); } } private void PatchGameMethod(string targetTypeName, string targetMethodName, MethodInfo prefixMethod, MethodInfo postfixMethod) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) try { if (prefixMethod == null && postfixMethod == null) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failure: Patch method not found - " + targetTypeName + "." + targetMethodName)); return; } Type type = AccessTools.TypeByName(targetTypeName); if (type == null) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failure: Target type not found - " + targetTypeName)); return; } MethodInfo methodInfo = AccessTools.Method(type, targetMethodName, (Type[])null, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failure: Target method not found - " + targetTypeName + "." + targetMethodName)); return; } HarmonyMethod val = ((!(prefixMethod != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(prefixMethod)); HarmonyMethod val2 = ((!(postfixMethod != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(postfixMethod)); harmony.Patch((MethodBase)methodInfo, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); WriteDebugLog("Patched: " + targetTypeName + "." + targetMethodName); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failure: PatchGameMethod " + targetTypeName + "." + targetMethodName + "\n" + ex)); } } private void BindConfig() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Expected O, but got Unknown //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Expected O, but got Unknown //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Expected O, but got Unknown //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Expected O, but got Unknown //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Expected O, but got Unknown //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Expected O, but got Unknown //IL_03e5: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Expected O, but got Unknown //IL_0422: Unknown result type (might be due to invalid IL or missing references) //IL_042c: Expected O, but got Unknown //IL_047f: Unknown result type (might be due to invalid IL or missing references) //IL_0489: Expected O, but got Unknown Enable = ((BaseUnityPlugin)this).Config.Bind("General", "Enable", true, "Enable this mod.このMODを有効化"); ShowDebugLog = ((BaseUnityPlugin)this).Config.Bind("General", "ShowDebugLog", false, "Show debug logs.デバッグログを表示"); SpeedMode = ((BaseUnityPlugin)this).Config.Bind("Speed", "SpeedMode", FastGachaSpeedMode.Fast, "Animation speed mode.演出速度モード"); AnimatorSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind("Speed", "AnimatorSpeedMultiplier", 3f, new ConfigDescription("Animator speed multiplier used by Fast mode. Fastモードで使用するAnimator速度倍率", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 30f), Array.Empty())); StateTimerMultiplier = ((BaseUnityPlugin)this).Config.Bind("Speed", "StateTimerMultiplier", 0.35f, new ConfigDescription("State timer multiplier used by Fast mode. Fastモードで使用するState待機時間倍率", (AcceptableValueBase)(object)new AcceptableValueRange(0.01f, 1f), Array.Empty())); SkipTokenOutro = ((BaseUnityPlugin)this).Config.Bind("Skip", "SkipTokenOutro", true, "Skip most of the token outro wait.トークン排出演出の待機をほぼ短縮"); SkipSpinWait = ((BaseUnityPlugin)this).Config.Bind("Skip", "SkipSpinWait", true, "Skip screen spin wait after token impact.トークン衝突後の画面回転待機を短縮"); SkipSEWait = ((BaseUnityPlugin)this).Config.Bind("Skip", "SkipSEWait", true, "Shorten waits that depend on animation or sound events.演出やSEイベント由来の待機を短縮"); SkipConfettiWait = ((BaseUnityPlugin)this).Config.Bind("Skip", "SkipConfettiWait", false, "Skip most of the confetti display time.コンフェッティ表示時間をほぼ短縮"); InstantResult = ((BaseUnityPlugin)this).Config.Bind("Skip", "InstantResult", false, "Force minimum result flow while keeping reward confirmation.報酬確定処理を残して最短化"); CosmeticResultDisplaySeconds = ((BaseUnityPlugin)this).Config.Bind("Result", "CosmeticResultDisplaySeconds", 1f, new ConfigDescription("Cosmetic result frame display seconds.コスメ報酬フレーム表示秒数", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 10f), Array.Empty())); CurrencyResultDisplaySeconds = ((BaseUnityPlugin)this).Config.Bind("Result", "CurrencyResultDisplaySeconds", 0.75f, new ConfigDescription("Currency result display seconds.通貨報酬表示秒数", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 10f), Array.Empty())); ReturnToIdleSeconds = ((BaseUnityPlugin)this).Config.Bind("Result", "ReturnToIdleSeconds", 0.15f, new ConfigDescription("Outro seconds before returning to idle.アイドル復帰前の終了待機秒数", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 5f), Array.Empty())); AccelerateAuthoritativeStateWhenHost = ((BaseUnityPlugin)this).Config.Bind("HostEffect", "AccelerateAuthoritativeStateWhenHost", true, "When host or singleplayer, shorten authoritative machine state timers.ホストまたはシングル時にガチャ機本体のState待機を短縮"); MinimumStateSeconds = ((BaseUnityPlugin)this).Config.Bind("Safety", "MinimumStateSeconds", 0.05f, new ConfigDescription("Minimum seconds kept for important states.重要Stateに残す最小秒数", (AcceptableValueBase)(object)new AcceptableValueRange(0.01f, 1f), Array.Empty())); ResetAnimatorSpeedOnIdle = ((BaseUnityPlugin)this).Config.Bind("Safety", "ResetAnimatorSpeedOnIdle", true, "Reset animator speed to 1 when the machine returns to idle.アイドル復帰時にAnimator速度を1へ戻す"); DoNotSkipRewardSetState = ((BaseUnityPlugin)this).Config.Bind("Safety", "DoNotSkipRewardSetState", true, "Do not fully skip states that confirm rewards.報酬確定Stateを完全スキップしない"); TurboMode = ((BaseUnityPlugin)this).Config.Bind("Turbo", "TurboMode", true, "Force more aggressive speed values even when old config values remain.既存Config値が残っていてもさらに高速化"); AllowQueuedMultiUse = ((BaseUnityPlugin)this).Config.Bind("MultiUse", "AllowQueuedMultiUse", true, "Allow multiple players to queue gacha uses while the machine is busy.ガチャ機が使用中でも複数人の利用要求をキュー化"); QueueLocalPresses = ((BaseUnityPlugin)this).Config.Bind("MultiUse", "QueueLocalPresses", true, "Queue local E presses while the machine is busy.ガチャ機が使用中でもローカルのE入力をキュー化"); QueueRemoteRequests = ((BaseUnityPlugin)this).Config.Bind("MultiUse", "QueueRemoteRequests", true, "Host queues remote player requests received while the machine is busy.ホストが使用中に受信した参加者の要求をキュー化"); MaxQueueSize = ((BaseUnityPlugin)this).Config.Bind("MultiUse", "MaxQueueSize", 32, new ConfigDescription("Maximum total queued gacha requests.全体の最大キュー数", (AcceptableValueBase)(object)new AcceptableValueRange(1, 200), Array.Empty())); MaxQueuedRequestsPerPlayer = ((BaseUnityPlugin)this).Config.Bind("MultiUse", "MaxQueuedRequestsPerPlayer", 10, new ConfigDescription("Maximum queued gacha requests per player.プレイヤーごとの最大キュー数", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); QueueProcessDelaySeconds = ((BaseUnityPlugin)this).Config.Bind("MultiUse", "QueueProcessDelaySeconds", 0.02f, new ConfigDescription("Delay before starting the next queued gacha after Idle.アイドル復帰後に次のキューを開始するまでの秒数", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); QueueInteractDistance = ((BaseUnityPlugin)this).Config.Bind("MultiUse", "QueueInteractDistance", 5f, new ConfigDescription("Maximum distance for local rapid E queue input.ローカルE連打キュー入力を許可する距離", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 20f), Array.Empty())); AllowRapidPress = ((BaseUnityPlugin)this).Config.Bind("RapidInput", "AllowRapidPress", true, "Allow repeated E presses to queue consecutive gacha uses. E連打で連続ガチャをキュー化"); RapidPressCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind("RapidInput", "RapidPressCooldownSeconds", 0.03f, new ConfigDescription("Minimum interval between rapid E queue inputs. E連打キュー入力の最小間隔", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); } internal static bool IsEnabled() { if (Enable != null && Enable.Value && SpeedMode != null) { return SpeedMode.Value != FastGachaSpeedMode.Vanilla; } return false; } internal static bool IsInstant() { if (!IsEnabled()) { return false; } if (InstantResult != null) { if (!InstantResult.Value) { return SpeedMode.Value == FastGachaSpeedMode.Instant; } return true; } return false; } internal static float GetAnimatorSpeed() { if (!IsEnabled()) { return 1f; } float num = ((AnimatorSpeedMultiplier != null) ? AnimatorSpeedMultiplier.Value : 3f); if (GetTurboMode()) { num = Mathf.Max(num, 15f); } return SpeedMode.Value switch { FastGachaSpeedMode.VeryFast => Mathf.Clamp(Mathf.Max(num, 6f), 1f, 30f), FastGachaSpeedMode.Instant => Mathf.Clamp(Mathf.Max(num, 20f), 1f, 30f), FastGachaSpeedMode.Fast => Mathf.Clamp(num, 1f, 30f), _ => 1f, }; } internal static float GetStateTimerMultiplier() { if (!IsEnabled()) { return 1f; } float num = ((StateTimerMultiplier != null) ? StateTimerMultiplier.Value : 0.35f); if (GetTurboMode()) { num = Mathf.Min(num, 0.05f); } return SpeedMode.Value switch { FastGachaSpeedMode.VeryFast => Mathf.Clamp(Mathf.Min(num, 0.18f), 0.01f, 1f), FastGachaSpeedMode.Instant => Mathf.Clamp(Mathf.Min(num, 0.05f), 0.01f, 1f), FastGachaSpeedMode.Fast => Mathf.Clamp(num, 0.01f, 1f), _ => 1f, }; } internal static float GetMinimumStateSeconds() { float num = ((MinimumStateSeconds != null) ? MinimumStateSeconds.Value : 0.05f); if (GetTurboMode()) { num = Mathf.Min(num, 0.01f); } return Mathf.Clamp(num, 0.01f, 1f); } internal static bool ShouldAccelerateAuthoritativeState() { if (!IsEnabled()) { return false; } if (AccelerateAuthoritativeStateWhenHost == null || !AccelerateAuthoritativeStateWhenHost.Value) { return false; } return GameReflection.IsMasterClientOrSingleplayerSafe(); } internal static float GetCurrencyDisplaySeconds() { float num = ((CurrencyResultDisplaySeconds != null) ? CurrencyResultDisplaySeconds.Value : 0.75f); if (!GetTurboMode()) { return num; } return Mathf.Min(num, 0.1f); } internal static float GetCosmeticDisplaySeconds() { float num = ((CosmeticResultDisplaySeconds != null) ? CosmeticResultDisplaySeconds.Value : 1f); if (!GetTurboMode()) { return num; } return Mathf.Min(num, 0.15f); } internal static float GetReturnToIdleSeconds() { float num = ((ReturnToIdleSeconds != null) ? ReturnToIdleSeconds.Value : 0.15f); if (!GetTurboMode()) { return num; } return Mathf.Min(num, 0.03f); } internal static bool GetSkipTokenOutro() { if (SkipTokenOutro != null) { return SkipTokenOutro.Value; } return false; } internal static bool GetSkipSpinWait() { if (SkipSpinWait != null) { return SkipSpinWait.Value; } return false; } internal static bool GetSkipSEWait() { if (SkipSEWait != null) { return SkipSEWait.Value; } return false; } internal static bool GetSkipConfettiWait() { if (SkipConfettiWait != null) { return SkipConfettiWait.Value; } return false; } internal static bool GetResetAnimatorSpeedOnIdle() { if (ResetAnimatorSpeedOnIdle != null) { return ResetAnimatorSpeedOnIdle.Value; } return true; } internal static bool GetDoNotSkipRewardSetState() { if (DoNotSkipRewardSetState != null) { return DoNotSkipRewardSetState.Value; } return true; } internal static bool GetTurboMode() { if (TurboMode != null) { return TurboMode.Value; } return false; } internal static bool GetAllowQueuedMultiUse() { if (AllowQueuedMultiUse != null) { return AllowQueuedMultiUse.Value; } return false; } internal static bool GetQueueLocalPresses() { if (QueueLocalPresses != null) { return QueueLocalPresses.Value; } return false; } internal static bool GetQueueRemoteRequests() { if (QueueRemoteRequests != null) { return QueueRemoteRequests.Value; } return false; } internal static int GetMaxQueueSize() { if (MaxQueueSize == null) { return 32; } return Mathf.Clamp(MaxQueueSize.Value, 1, 200); } internal static int GetMaxQueuedRequestsPerPlayer() { if (MaxQueuedRequestsPerPlayer == null) { return 10; } return Mathf.Clamp(MaxQueuedRequestsPerPlayer.Value, 1, 100); } internal static float GetQueueProcessDelaySeconds() { if (QueueProcessDelaySeconds == null) { return 0.02f; } return Mathf.Clamp(QueueProcessDelaySeconds.Value, 0f, 1f); } internal static float GetQueueInteractDistance() { if (QueueInteractDistance == null) { return 5f; } return Mathf.Clamp(QueueInteractDistance.Value, 1f, 20f); } internal static bool GetAllowRapidPress() { if (AllowRapidPress != null) { return AllowRapidPress.Value; } return false; } internal static float GetRapidPressCooldownSeconds() { if (RapidPressCooldownSeconds == null) { return 0.03f; } return Mathf.Clamp(RapidPressCooldownSeconds.Value, 0f, 1f); } internal static void WriteDebugLog(string message) { if (Log != null && ShowDebugLog != null && ShowDebugLog.Value) { Log.LogInfo((object)message); } } internal static void WriteFailureLog(string message, Exception ex) { if (Log != null) { Log.LogError((object)("Failure: " + message + "\n" + ex)); } } } public enum FastGachaSpeedMode { Vanilla, Fast, VeryFast, Instant } internal static class GameReflection { private static Type semiFuncType; private static Type inputKeyType; private static Type rpcTargetType; private static Type metaManagerType; private static MethodInfo semiFuncIsMasterClientOrSingleplayerMethod; private static MethodInfo semiFuncIsMultiplayerMethod; private static MethodInfo semiFuncPlayerGetLocalMethod; private static MethodInfo semiFuncPlayerGetListMethod; private static MethodInfo semiFuncInputDownMethod; private static FieldInfo metaManagerInstanceField; private static FieldInfo metaManagerCosmeticTokensField; private static FieldInfo machineStateCurrentField; private static FieldInfo machineStateTimerField; private static FieldInfo machineAnimatorField; private static FieldInfo machinePhotonViewField; private static FieldInfo machineInteractingPlayerField; private static MethodInfo machineInteractSetMethod; private static FieldInfo machineScreenSpinStoppedField; private static FieldInfo machineScreenSpinStartedField; private static FieldInfo machineScreenSpinSpeedField; private static FieldInfo machineScreenSpinTimeField; private static FieldInfo machineScreenSpinSettleLerpField; private static FieldInfo machineScreenSpinningFastField; private static FieldInfo machineScreenSpinTransformField; private static FieldInfo machineScreenSpinSettleTransformField; private static FieldInfo animatorUnityAnimatorField; private static FieldInfo animatorControllerField; private static FieldInfo animatorTokenIntroDoneField; private static FieldInfo animatorTokenImpactDoneField; private static FieldInfo confettiActiveTimerField; private static FieldInfo playerAvatarPhotonViewField; private static PropertyInfo photonViewViewIDProperty; private static PropertyInfo photonViewOwnerProperty; private static MethodInfo photonViewRpcMethod; private static PropertyInfo photonMessageInfoSenderProperty; internal static void Initialize() { Type type = AccessTools.TypeByName("CosmeticShopMachine"); Type type2 = AccessTools.TypeByName("CosmeticShopMachineAnimator"); Type type3 = AccessTools.TypeByName("CosmeticShopMachineConfetti"); Type type4 = AccessTools.TypeByName("PlayerAvatar"); Type type5 = AccessTools.TypeByName("Photon.Pun.PhotonView"); Type type6 = AccessTools.TypeByName("Photon.Pun.PhotonMessageInfo"); inputKeyType = AccessTools.TypeByName("InputKey"); rpcTargetType = AccessTools.TypeByName("Photon.Pun.RpcTarget"); metaManagerType = AccessTools.TypeByName("MetaManager"); semiFuncType = AccessTools.TypeByName("SemiFunc"); if (semiFuncType != null) { semiFuncIsMasterClientOrSingleplayerMethod = AccessTools.Method(semiFuncType, "IsMasterClientOrSingleplayer", (Type[])null, (Type[])null); semiFuncIsMultiplayerMethod = AccessTools.Method(semiFuncType, "IsMultiplayer", (Type[])null, (Type[])null); semiFuncPlayerGetLocalMethod = AccessTools.Method(semiFuncType, "PlayerGetLocal", (Type[])null, (Type[])null); semiFuncPlayerGetListMethod = AccessTools.Method(semiFuncType, "PlayerGetList", (Type[])null, (Type[])null); semiFuncInputDownMethod = AccessTools.Method(semiFuncType, "InputDown", (Type[])null, (Type[])null); } if (metaManagerType != null) { metaManagerInstanceField = AccessTools.Field(metaManagerType, "instance"); metaManagerCosmeticTokensField = AccessTools.Field(metaManagerType, "cosmeticTokens"); } if (type5 != null) { photonViewViewIDProperty = AccessTools.Property(type5, "ViewID"); photonViewOwnerProperty = AccessTools.Property(type5, "Owner"); if (rpcTargetType != null) { photonViewRpcMethod = AccessTools.Method(type5, "RPC", new Type[3] { typeof(string), rpcTargetType, typeof(object[]) }, (Type[])null); } } if (type6 != null) { photonMessageInfoSenderProperty = AccessTools.Property(type6, "Sender"); } if (type4 != null) { playerAvatarPhotonViewField = AccessTools.Field(type4, "photonView"); } if (type != null) { machineStateCurrentField = AccessTools.Field(type, "stateCurrent"); machineStateTimerField = AccessTools.Field(type, "stateTimer"); machineAnimatorField = AccessTools.Field(type, "animator"); machinePhotonViewField = AccessTools.Field(type, "photonView"); machineInteractingPlayerField = AccessTools.Field(type, "interactingPlayer"); machineInteractSetMethod = AccessTools.Method(type, "InteractSet", (Type[])null, (Type[])null); machineScreenSpinStoppedField = AccessTools.Field(type, "screenSpinStopped"); machineScreenSpinStartedField = AccessTools.Field(type, "screenSpinStarted"); machineScreenSpinSpeedField = AccessTools.Field(type, "screenSpinSpeed"); machineScreenSpinTimeField = AccessTools.Field(type, "screenSpinTime"); machineScreenSpinSettleLerpField = AccessTools.Field(type, "screenSpinSettleLerp"); machineScreenSpinningFastField = AccessTools.Field(type, "screenSpinningFast"); machineScreenSpinTransformField = AccessTools.Field(type, "screenSpinTransform"); machineScreenSpinSettleTransformField = AccessTools.Field(type, "screenSpinSettleTransform"); } if (type2 != null) { animatorUnityAnimatorField = AccessTools.Field(type2, "animator"); animatorControllerField = AccessTools.Field(type2, "controller"); animatorTokenIntroDoneField = AccessTools.Field(type2, "tokenIntroDone"); animatorTokenImpactDoneField = AccessTools.Field(type2, "tokenImpactDone"); } if (type3 != null) { confettiActiveTimerField = AccessTools.Field(type3, "activeTimer"); } } internal static bool IsMasterClientOrSingleplayerSafe() { try { if (semiFuncIsMasterClientOrSingleplayerMethod == null) { return false; } object obj = semiFuncIsMasterClientOrSingleplayerMethod.Invoke(null, null); return obj is bool && (bool)obj; } catch { return false; } } internal static bool IsMultiplayerSafe() { try { if (semiFuncIsMultiplayerMethod == null) { return false; } object obj = semiFuncIsMultiplayerMethod.Invoke(null, null); return obj is bool && (bool)obj; } catch { return false; } } internal static bool IsInteractInputDownSafe() { try { if (semiFuncInputDownMethod == null || inputKeyType == null) { return false; } object obj = Enum.Parse(inputKeyType, "Interact"); object obj2 = semiFuncInputDownMethod.Invoke(null, new object[1] { obj }); return obj2 is bool && (bool)obj2; } catch { return false; } } internal static object GetLocalPlayer() { try { if (semiFuncPlayerGetLocalMethod == null) { return null; } return semiFuncPlayerGetLocalMethod.Invoke(null, null); } catch { return null; } } internal static IEnumerable GetPlayerList() { try { if (semiFuncPlayerGetListMethod == null) { return null; } return semiFuncPlayerGetListMethod.Invoke(null, null) as IEnumerable; } catch { return null; } } internal static string GetStateName(object state) { if (state == null) { return string.Empty; } return state.ToString(); } internal static string GetCurrentStateName(object machine) { try { if (machine == null || machineStateCurrentField == null) { return "Idle"; } return GetStateName(machineStateCurrentField.GetValue(machine)); } catch { return "Idle"; } } internal static object GetAnimatorController(object animator) { try { if (animator == null || animatorControllerField == null) { return null; } return animatorControllerField.GetValue(animator); } catch { return null; } } internal static object GetMachineAnimator(object machine) { try { if (machine == null || machineAnimatorField == null) { return null; } return machineAnimatorField.GetValue(machine); } catch { return null; } } internal static float GetStateTimer(object machine) { try { if (machine == null || machineStateTimerField == null) { return 0f; } object value = machineStateTimerField.GetValue(machine); return (value is float) ? ((float)value) : 0f; } catch { return 0f; } } internal static object GetInteractingPlayer(object machine) { try { if (machine == null || machineInteractingPlayerField == null) { return null; } return machineInteractingPlayerField.GetValue(machine); } catch { return null; } } internal static bool IsSamePlayer(object left, object right) { if (left == null || right == null) { return false; } return GetPlayerViewId(left) == GetPlayerViewId(right); } internal static int GetPlayerViewId(object player) { try { object playerPhotonView = GetPlayerPhotonView(player); if (playerPhotonView == null || photonViewViewIDProperty == null) { return -1; } object value = photonViewViewIDProperty.GetValue(playerPhotonView, null); return (value is int) ? ((int)value) : (-1); } catch { return -1; } } internal static object GetPlayerPhotonView(object player) { try { if (player == null || playerAvatarPhotonViewField == null) { return null; } return playerAvatarPhotonViewField.GetValue(player); } catch { return null; } } internal static object GetMachinePhotonView(object machine) { try { if (machine == null || machinePhotonViewField == null) { return null; } return machinePhotonViewField.GetValue(machine); } catch { return null; } } internal static object GetPhotonMessageSender(object photonMessageInfo) { try { if (photonMessageInfo == null || photonMessageInfoSenderProperty == null) { return null; } return photonMessageInfoSenderProperty.GetValue(photonMessageInfo, null); } catch { return null; } } internal static object FindPlayerBySenderInfo(object photonMessageInfo) { try { object photonMessageSender = GetPhotonMessageSender(photonMessageInfo); if (photonMessageSender == null) { return null; } IEnumerable playerList = GetPlayerList(); if (playerList == null) { return null; } foreach (object item in playerList) { object playerPhotonView = GetPlayerPhotonView(item); if (playerPhotonView != null && !(photonViewOwnerProperty == null)) { object value = photonViewOwnerProperty.GetValue(playerPhotonView, null); if (object.Equals(value, photonMessageSender)) { return item; } } } return null; } catch { return null; } } internal static int GetLocalTokenCount() { try { return GetCosmeticTokens()?.Count ?? 0; } catch { return 0; } } internal static int GetLocalTokenRarityAtOffset(int pendingOffset) { try { IList cosmeticTokens = GetCosmeticTokens(); if (cosmeticTokens == null || cosmeticTokens.Count == 0) { return -1; } int num = cosmeticTokens.Count - 1 - Mathf.Max(0, pendingOffset); if (num < 0 || num >= cosmeticTokens.Count) { return -1; } object obj = cosmeticTokens[num]; return (obj is int) ? ((int)obj) : (-1); } catch { return -1; } } private static IList GetCosmeticTokens() { try { if (metaManagerInstanceField == null || metaManagerCosmeticTokensField == null) { return null; } object value = metaManagerInstanceField.GetValue(null); if (value == null) { return null; } return metaManagerCosmeticTokensField.GetValue(value) as IList; } catch { return null; } } internal static bool SendInteractRpcToMaster(object machine, int rarity) { try { object machinePhotonView = GetMachinePhotonView(machine); if (machinePhotonView == null || photonViewRpcMethod == null || rpcTargetType == null) { return false; } object obj = Enum.Parse(rpcTargetType, "MasterClient"); photonViewRpcMethod.Invoke(machinePhotonView, new object[3] { "InteractClientRPC", obj, new object[1] { rarity } }); return true; } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("SendInteractRpcToMaster", ex); return false; } } internal static bool InvokeInteractSet(object machine, object player, int rarity) { try { if (machine == null || player == null || machineInteractSetMethod == null) { return false; } machineInteractSetMethod.Invoke(machine, new object[2] { player, rarity }); return true; } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("InvokeInteractSet", ex); return false; } } internal static bool IsLocalPlayerNearMachine(object machine) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) try { object localPlayer = GetLocalPlayer(); Component val = (Component)((localPlayer is Component) ? localPlayer : null); Component val2 = (Component)((machine is Component) ? machine : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return false; } float num = Vector3.Distance(val.transform.position, val2.transform.position); return num <= FastCosmeticGachaPlugin.GetQueueInteractDistance(); } catch { return false; } } internal static void SetStateTimer(object machine, float value) { try { if (machine != null && !(machineStateTimerField == null)) { machineStateTimerField.SetValue(machine, value); } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("SetStateTimer", ex); } } internal static void SetTokenIntroDone(object animator, bool value) { try { if (animator != null && !(animatorTokenIntroDoneField == null)) { animatorTokenIntroDoneField.SetValue(animator, value); } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("SetTokenIntroDone", ex); } } internal static void SetTokenImpactDone(object animator, bool value) { try { if (animator != null && !(animatorTokenImpactDoneField == null)) { animatorTokenImpactDoneField.SetValue(animator, value); } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("SetTokenImpactDone", ex); } } internal static Animator GetUnityAnimator(object animator) { try { if (animator == null || animatorUnityAnimatorField == null) { return null; } object? value = animatorUnityAnimatorField.GetValue(animator); return (Animator)((value is Animator) ? value : null); } catch { return null; } } internal static void SetConfettiActiveTimer(object confetti, float value) { try { if (confetti != null && !(confettiActiveTimerField == null)) { confettiActiveTimerField.SetValue(confetti, value); } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("SetConfettiActiveTimer", ex); } } internal static void ForceScreenStop(object machine) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) try { if (machine != null) { SetFieldValue(machineScreenSpinStoppedField, machine, true); SetFieldValue(machineScreenSpinStartedField, machine, false); SetFieldValue(machineScreenSpinningFastField, machine, false); SetFieldValue(machineScreenSpinSpeedField, machine, 0f); SetFieldValue(machineScreenSpinTimeField, machine, 0f); SetFieldValue(machineScreenSpinSettleLerpField, machine, 1f); object fieldValue = GetFieldValue(machineScreenSpinTransformField, machine); Transform val = (Transform)((fieldValue is Transform) ? fieldValue : null); if ((Object)(object)val != (Object)null) { val.localRotation = Quaternion.identity; } object fieldValue2 = GetFieldValue(machineScreenSpinSettleTransformField, machine); Transform val2 = (Transform)((fieldValue2 is Transform) ? fieldValue2 : null); if ((Object)(object)val2 != (Object)null) { val2.localEulerAngles = new Vector3(0f, 20f, 0f); } } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("ForceScreenStop", ex); } } private static void SetFieldValue(FieldInfo field, object instance, object value) { if (field != null && instance != null) { field.SetValue(instance, value); } } private static object GetFieldValue(FieldInfo field, object instance) { if (field == null || instance == null) { return null; } return field.GetValue(instance); } } internal static class StateUtility { internal static bool IsGachaActiveState(string stateName) { if (!string.IsNullOrEmpty(stateName)) { return stateName != "Idle"; } return false; } internal static bool IsRewardSetState(string stateName) { if (!(stateName == "RewardCosmeticIntro")) { return stateName == "RewardCurrency"; } return true; } internal static bool CanSkipAnimatorWait(string stateName) { switch (stateName) { default: return stateName == "RewardCosmeticFrameOutro"; case "FetchRewardDone": case "TokenOutro": case "RewardCurrencyIntro": case "RewardCosmeticIntro": case "RewardCosmeticConfetti": case "RewardCosmeticFrame": return true; } } internal static float GetShortenedTimer(string stateName, float originalSeconds) { float num = FastCosmeticGachaPlugin.GetMinimumStateSeconds(); float stateTimerMultiplier = FastCosmeticGachaPlugin.GetStateTimerMultiplier(); bool flag = FastCosmeticGachaPlugin.IsInstant(); if (!FastCosmeticGachaPlugin.IsEnabled()) { return originalSeconds; } if (stateName == "FetchReward") { return originalSeconds; } if (FastCosmeticGachaPlugin.GetDoNotSkipRewardSetState() && IsRewardSetState(stateName)) { num = Mathf.Max(num, 0.05f); } switch (stateName) { case "Interact": return Mathf.Max(num, originalSeconds * stateTimerMultiplier); case "FetchRewardDone": if (flag || FastCosmeticGachaPlugin.GetSkipSEWait() || FastCosmeticGachaPlugin.GetSkipSpinWait()) { return num; } return Mathf.Max(num, originalSeconds * stateTimerMultiplier); case "TokenOutro": if (flag || FastCosmeticGachaPlugin.GetSkipTokenOutro()) { return num; } return Mathf.Max(num, originalSeconds * stateTimerMultiplier); case "RewardCurrencyIntro": if (flag) { return num; } return Mathf.Max(num, originalSeconds * stateTimerMultiplier); case "RewardCurrency": if (flag) { return num; } return Mathf.Max(num, FastCosmeticGachaPlugin.GetCurrencyDisplaySeconds()); case "RewardCurrencyOutro": return Mathf.Max(num, FastCosmeticGachaPlugin.GetReturnToIdleSeconds()); case "RewardCosmeticIntro": if (flag) { return num; } return Mathf.Max(num, originalSeconds * stateTimerMultiplier); case "RewardCosmeticConfetti": if (flag || FastCosmeticGachaPlugin.GetSkipConfettiWait()) { return num; } return Mathf.Max(num, originalSeconds * stateTimerMultiplier); case "RewardCosmeticFrame": if (flag) { return num; } return Mathf.Max(num, FastCosmeticGachaPlugin.GetCosmeticDisplaySeconds()); case "RewardCosmeticFrameOutro": if (flag) { return num; } return Mathf.Max(num, originalSeconds * stateTimerMultiplier); case "RewardCosmeticOutro": return Mathf.Max(num, FastCosmeticGachaPlugin.GetReturnToIdleSeconds()); default: return Mathf.Max(num, originalSeconds * stateTimerMultiplier); } } internal static float GetKnownOriginalTimer(string stateName) { return stateName switch { "Interact" => 0.1f, "FetchRewardDone" => 0.1f, "TokenOutro" => 1f, "RewardCurrencyIntro" => 1f, "RewardCurrency" => 1f, "RewardCurrencyOutro" => 0.5f, "RewardCosmeticIntro" => 1.5f, "RewardCosmeticConfetti" => 1.5f, "RewardCosmeticFrame" => 2f, "RewardCosmeticFrameOutro" => 1f, "RewardCosmeticOutro" => 0.5f, _ => 0f, }; } } internal sealed class QueuedGachaRequest { internal object Player; internal int PlayerViewId; internal int Rarity; internal bool LocalRequest; internal float CreatedTime; internal string Source; } internal static class QueuedGachaManager { private static readonly List PendingRequests = new List(); private static int localPendingRequests; private static bool localQueuedRequestActive; private static float lastLocalPressTime = -999f; private static float nextProcessTime; internal static void CaptureLocalPress(object machine, string stateBeforeUpdate) { try { if (!FastCosmeticGachaPlugin.IsEnabled() || !FastCosmeticGachaPlugin.GetAllowQueuedMultiUse() || !FastCosmeticGachaPlugin.GetAllowRapidPress() || !FastCosmeticGachaPlugin.GetQueueLocalPresses() || machine == null || stateBeforeUpdate == "Idle" || Time.time - lastLocalPressTime < FastCosmeticGachaPlugin.GetRapidPressCooldownSeconds() || !GameReflection.IsInteractInputDownSafe() || !GameReflection.IsLocalPlayerNearMachine(machine)) { return; } int localTokenCount = GameReflection.GetLocalTokenCount(); int localActiveUnconsumedOffset = GetLocalActiveUnconsumedOffset(machine, stateBeforeUpdate); int num = localActiveUnconsumedOffset + localPendingRequests; if (localTokenCount <= 0 || num >= localTokenCount) { return; } int localTokenRarityAtOffset = GameReflection.GetLocalTokenRarityAtOffset(num); if (!IsValidRarity(localTokenRarityAtOffset)) { return; } object localPlayer = GameReflection.GetLocalPlayer(); if (localPlayer == null) { return; } if (GameReflection.IsMasterClientOrSingleplayerSafe()) { if (Enqueue(machine, localPlayer, localTokenRarityAtOffset, localRequest: true, "LocalHost")) { lastLocalPressTime = Time.time; } } else if (GameReflection.SendInteractRpcToMaster(machine, localTokenRarityAtOffset)) { localPendingRequests++; lastLocalPressTime = Time.time; FastCosmeticGachaPlugin.WriteDebugLog("Rapid request sent to host. PendingLocal=" + localPendingRequests); } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("CaptureLocalPress", ex); } } internal static bool TryQueueRemoteRequest(object machine, object[] args) { try { if (!FastCosmeticGachaPlugin.IsEnabled() || !FastCosmeticGachaPlugin.GetAllowQueuedMultiUse() || !FastCosmeticGachaPlugin.GetQueueRemoteRequests()) { return false; } if (!GameReflection.IsMasterClientOrSingleplayerSafe()) { return false; } if (machine == null || args == null || args.Length < 2) { return false; } string currentStateName = GameReflection.GetCurrentStateName(machine); if (currentStateName == "Idle") { return false; } int rarity = ((args[0] is int) ? ((int)args[0]) : (-1)); if (!IsValidRarity(rarity)) { return true; } object obj = GameReflection.FindPlayerBySenderInfo(args[1]); if (obj == null) { return true; } Enqueue(machine, obj, rarity, localRequest: false, "RemoteRPC"); return true; } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("TryQueueRemoteRequest", ex); return false; } } internal static void ProcessQueue(object machine) { try { if (!FastCosmeticGachaPlugin.IsEnabled() || !FastCosmeticGachaPlugin.GetAllowQueuedMultiUse()) { PendingRequests.Clear(); } else { if (!GameReflection.IsMasterClientOrSingleplayerSafe() || machine == null || PendingRequests.Count == 0 || Time.time < nextProcessTime) { return; } string currentStateName = GameReflection.GetCurrentStateName(machine); if (currentStateName != "Idle") { return; } while (PendingRequests.Count > 0) { QueuedGachaRequest queuedGachaRequest = PendingRequests[0]; PendingRequests.RemoveAt(0); if (queuedGachaRequest != null && queuedGachaRequest.Player != null && IsValidRarity(queuedGachaRequest.Rarity) && GameReflection.InvokeInteractSet(machine, queuedGachaRequest.Player, queuedGachaRequest.Rarity)) { nextProcessTime = Time.time + FastCosmeticGachaPlugin.GetQueueProcessDelaySeconds(); FastCosmeticGachaPlugin.WriteDebugLog("Queued gacha started. PlayerViewID=" + queuedGachaRequest.PlayerViewId + " Rarity=" + queuedGachaRequest.Rarity + " Remaining=" + PendingRequests.Count); break; } } } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("ProcessQueue", ex); } } internal static void MarkLocalQueuedRequestStarted(object machine) { try { if (localPendingRequests > 0) { object interactingPlayer = GameReflection.GetInteractingPlayer(machine); object localPlayer = GameReflection.GetLocalPlayer(); if (GameReflection.IsSamePlayer(interactingPlayer, localPlayer)) { localQueuedRequestActive = true; FastCosmeticGachaPlugin.WriteDebugLog("Local queued request started."); } } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("MarkLocalQueuedRequestStarted", ex); } } internal static void RewardSetPostfix(object machine) { try { object interactingPlayer = GameReflection.GetInteractingPlayer(machine); object localPlayer = GameReflection.GetLocalPlayer(); if (GameReflection.IsSamePlayer(interactingPlayer, localPlayer) && localQueuedRequestActive && localPendingRequests > 0) { localPendingRequests--; localQueuedRequestActive = false; FastCosmeticGachaPlugin.WriteDebugLog("Local queued pending consumed. PendingLocal=" + localPendingRequests); } if (GameReflection.GetLocalTokenCount() == 0) { localPendingRequests = 0; localQueuedRequestActive = false; } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("RewardSetPostfix", ex); } } private static bool Enqueue(object machine, object player, int rarity, bool localRequest, string source) { if (machine == null || player == null || !IsValidRarity(rarity)) { return false; } if (PendingRequests.Count >= FastCosmeticGachaPlugin.GetMaxQueueSize()) { return false; } int playerViewId = GameReflection.GetPlayerViewId(player); int num = 0; for (int i = 0; i < PendingRequests.Count; i++) { if (PendingRequests[i] != null && PendingRequests[i].PlayerViewId == playerViewId) { num++; } } if (num >= FastCosmeticGachaPlugin.GetMaxQueuedRequestsPerPlayer()) { return false; } if (localRequest) { int localTokenCount = GameReflection.GetLocalTokenCount(); if (localTokenCount <= 0 || localPendingRequests >= localTokenCount) { return false; } localPendingRequests++; } PendingRequests.Add(new QueuedGachaRequest { Player = player, PlayerViewId = playerViewId, Rarity = rarity, LocalRequest = localRequest, CreatedTime = Time.time, Source = source }); FastCosmeticGachaPlugin.WriteDebugLog("Queued gacha request. Source=" + source + " PlayerViewID=" + playerViewId + " Rarity=" + rarity + " Queue=" + PendingRequests.Count); return true; } private static int GetLocalActiveUnconsumedOffset(object machine, string stateName) { if (machine == null || string.IsNullOrEmpty(stateName)) { return 0; } if (stateName != "Interact" && stateName != "FetchReward" && stateName != "FetchRewardDone" && stateName != "TokenOutro" && stateName != "RewardCurrencyIntro") { return 0; } object interactingPlayer = GameReflection.GetInteractingPlayer(machine); object localPlayer = GameReflection.GetLocalPlayer(); return GameReflection.IsSamePlayer(interactingPlayer, localPlayer) ? 1 : 0; } private static bool IsValidRarity(int rarity) { if (rarity >= 0) { return rarity <= 3; } return false; } } internal static class MachineUpdateHook { private static void Prefix(object __instance, out string __state) { __state = GameReflection.GetCurrentStateName(__instance); } private static void Postfix(object __instance, string __state) { try { if (__instance != null) { QueuedGachaManager.CaptureLocalPress(__instance, __state); QueuedGachaManager.ProcessQueue(__instance); } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("Machine.Update Postfix", ex); } } } internal static class InteractClientRpcHook { private static bool Prefix(object __instance, object[] __args) { try { if (QueuedGachaManager.TryQueueRemoteRequest(__instance, __args)) { return false; } return true; } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("InteractClientRPC Prefix", ex); return true; } } } internal static class RewardSetHook { private static void Postfix(object __instance) { QueuedGachaManager.RewardSetPostfix(__instance); } } internal static class StateRpcHook { private static void Postfix(object __instance, object[] __args) { try { if (__instance == null || !FastCosmeticGachaPlugin.IsEnabled()) { return; } string text = string.Empty; if (__args != null && __args.Length != 0) { text = GameReflection.GetStateName(__args[0]); } if (string.IsNullOrEmpty(text)) { text = GameReflection.GetCurrentStateName(__instance); } if (text == "Interact") { QueuedGachaManager.MarkLocalQueuedRequestStarted(__instance); } TryCompleteAnimationWaits(__instance, text); if (FastCosmeticGachaPlugin.ShouldAccelerateAuthoritativeState()) { float knownOriginalTimer = StateUtility.GetKnownOriginalTimer(text); if (!(knownOriginalTimer <= 0f)) { float shortenedTimer = StateUtility.GetShortenedTimer(text, knownOriginalTimer); GameReflection.SetStateTimer(__instance, shortenedTimer); FastCosmeticGachaPlugin.WriteDebugLog("State shortened: " + text + " " + knownOriginalTimer.ToString("0.00") + " -> " + shortenedTimer.ToString("0.00")); } } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("UpdateStateRPC Postfix", ex); } } private static void TryCompleteAnimationWaits(object machine, string stateName) { if (machine == null) { return; } object machineAnimator = GameReflection.GetMachineAnimator(machine); if (machineAnimator != null) { bool flag = FastCosmeticGachaPlugin.IsInstant(); if (stateName == "FetchRewardDone" && (flag || FastCosmeticGachaPlugin.GetSkipSEWait() || FastCosmeticGachaPlugin.GetSkipSpinWait())) { GameReflection.SetTokenIntroDone(machineAnimator, value: true); } if (StateUtility.CanSkipAnimatorWait(stateName) && (flag || FastCosmeticGachaPlugin.GetSkipSpinWait())) { GameReflection.SetTokenImpactDone(machineAnimator, value: true); GameReflection.ForceScreenStop(machine); } } } } internal static class AnimatorUpdateHook { private static void Postfix(object __instance) { try { if (__instance == null) { return; } Animator unityAnimator = GameReflection.GetUnityAnimator(__instance); if ((Object)(object)unityAnimator == (Object)null) { return; } object animatorController = GameReflection.GetAnimatorController(__instance); string currentStateName = GameReflection.GetCurrentStateName(animatorController); if (!FastCosmeticGachaPlugin.IsEnabled() || !StateUtility.IsGachaActiveState(currentStateName)) { if (FastCosmeticGachaPlugin.GetResetAnimatorSpeedOnIdle()) { unityAnimator.speed = 1f; } return; } unityAnimator.speed = FastCosmeticGachaPlugin.GetAnimatorSpeed(); bool flag = FastCosmeticGachaPlugin.IsInstant(); if (currentStateName == "FetchRewardDone" && (flag || FastCosmeticGachaPlugin.GetSkipSEWait() || FastCosmeticGachaPlugin.GetSkipSpinWait())) { GameReflection.SetTokenIntroDone(__instance, value: true); } if (StateUtility.CanSkipAnimatorWait(currentStateName) && (flag || FastCosmeticGachaPlugin.GetSkipSpinWait())) { GameReflection.SetTokenImpactDone(__instance, value: true); GameReflection.ForceScreenStop(animatorController); } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("Animator.Update Postfix", ex); } } } internal static class ScreenSpinHook { private static void Postfix(object __instance) { try { if (__instance == null || !FastCosmeticGachaPlugin.IsEnabled()) { return; } string currentStateName = GameReflection.GetCurrentStateName(__instance); if (StateUtility.CanSkipAnimatorWait(currentStateName) && (FastCosmeticGachaPlugin.IsInstant() || FastCosmeticGachaPlugin.GetSkipSpinWait())) { object machineAnimator = GameReflection.GetMachineAnimator(__instance); if (machineAnimator != null) { GameReflection.SetTokenImpactDone(machineAnimator, value: true); } GameReflection.ForceScreenStop(__instance); } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("ScreenSpinLogic Postfix", ex); } } } internal static class ConfettiSetHook { private static void Postfix(object __instance) { try { if (__instance != null && FastCosmeticGachaPlugin.IsEnabled()) { float minimumStateSeconds = FastCosmeticGachaPlugin.GetMinimumStateSeconds(); float value = ((!FastCosmeticGachaPlugin.IsInstant() && !FastCosmeticGachaPlugin.GetSkipConfettiWait()) ? Mathf.Max(minimumStateSeconds, 3f * FastCosmeticGachaPlugin.GetStateTimerMultiplier()) : minimumStateSeconds); GameReflection.SetConfettiActiveTimer(__instance, value); FastCosmeticGachaPlugin.WriteDebugLog("Confetti shortened: 3.00 -> " + value.ToString("0.00")); } } catch (Exception ex) { FastCosmeticGachaPlugin.WriteFailureLog("Confetti.Set Postfix", ex); } } } }