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 FastCosmeticGacha v4.0.0
FastCosmeticGacha.dll
Decompiled a week agousing 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<bool> Enable; internal static ConfigEntry<bool> ShowDebugLog; internal static ConfigEntry<FastGachaSpeedMode> SpeedMode; internal static ConfigEntry<float> AnimatorSpeedMultiplier; internal static ConfigEntry<float> StateTimerMultiplier; internal static ConfigEntry<bool> SkipTokenOutro; internal static ConfigEntry<bool> SkipSpinWait; internal static ConfigEntry<bool> SkipSEWait; internal static ConfigEntry<bool> SkipConfettiWait; internal static ConfigEntry<bool> InstantResult; internal static ConfigEntry<float> CosmeticResultDisplaySeconds; internal static ConfigEntry<float> CurrencyResultDisplaySeconds; internal static ConfigEntry<float> ReturnToIdleSeconds; internal static ConfigEntry<bool> AccelerateAuthoritativeStateWhenHost; internal static ConfigEntry<float> MinimumStateSeconds; internal static ConfigEntry<bool> ResetAnimatorSpeedOnIdle; internal static ConfigEntry<bool> DoNotSkipRewardSetState; internal static ConfigEntry<bool> TurboMode; internal static ConfigEntry<bool> AllowQueuedMultiUse; internal static ConfigEntry<bool> QueueLocalPresses; internal static ConfigEntry<bool> QueueRemoteRequests; internal static ConfigEntry<int> MaxQueueSize; internal static ConfigEntry<int> MaxQueuedRequestsPerPlayer; internal static ConfigEntry<float> QueueProcessDelaySeconds; internal static ConfigEntry<float> QueueInteractDistance; internal static ConfigEntry<bool> AllowRapidPress; internal static ConfigEntry<float> 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<bool>("General", "Enable", true, "Enable this mod.このMODを有効化"); ShowDebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowDebugLog", false, "Show debug logs.デバッグログを表示"); SpeedMode = ((BaseUnityPlugin)this).Config.Bind<FastGachaSpeedMode>("Speed", "SpeedMode", FastGachaSpeedMode.Fast, "Animation speed mode.演出速度モード"); AnimatorSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "AnimatorSpeedMultiplier", 3f, new ConfigDescription("Animator speed multiplier used by Fast mode. Fastモードで使用するAnimator速度倍率", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>())); StateTimerMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "StateTimerMultiplier", 0.35f, new ConfigDescription("State timer multiplier used by Fast mode. Fastモードで使用するState待機時間倍率", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); SkipTokenOutro = ((BaseUnityPlugin)this).Config.Bind<bool>("Skip", "SkipTokenOutro", true, "Skip most of the token outro wait.トークン排出演出の待機をほぼ短縮"); SkipSpinWait = ((BaseUnityPlugin)this).Config.Bind<bool>("Skip", "SkipSpinWait", true, "Skip screen spin wait after token impact.トークン衝突後の画面回転待機を短縮"); SkipSEWait = ((BaseUnityPlugin)this).Config.Bind<bool>("Skip", "SkipSEWait", true, "Shorten waits that depend on animation or sound events.演出やSEイベント由来の待機を短縮"); SkipConfettiWait = ((BaseUnityPlugin)this).Config.Bind<bool>("Skip", "SkipConfettiWait", false, "Skip most of the confetti display time.コンフェッティ表示時間をほぼ短縮"); InstantResult = ((BaseUnityPlugin)this).Config.Bind<bool>("Skip", "InstantResult", false, "Force minimum result flow while keeping reward confirmation.報酬確定処理を残して最短化"); CosmeticResultDisplaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Result", "CosmeticResultDisplaySeconds", 1f, new ConfigDescription("Cosmetic result frame display seconds.コスメ報酬フレーム表示秒数", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f), Array.Empty<object>())); CurrencyResultDisplaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Result", "CurrencyResultDisplaySeconds", 0.75f, new ConfigDescription("Currency result display seconds.通貨報酬表示秒数", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f), Array.Empty<object>())); ReturnToIdleSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Result", "ReturnToIdleSeconds", 0.15f, new ConfigDescription("Outro seconds before returning to idle.アイドル復帰前の終了待機秒数", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), Array.Empty<object>())); AccelerateAuthoritativeStateWhenHost = ((BaseUnityPlugin)this).Config.Bind<bool>("HostEffect", "AccelerateAuthoritativeStateWhenHost", true, "When host or singleplayer, shorten authoritative machine state timers.ホストまたはシングル時にガチャ機本体のState待機を短縮"); MinimumStateSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "MinimumStateSeconds", 0.05f, new ConfigDescription("Minimum seconds kept for important states.重要Stateに残す最小秒数", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); ResetAnimatorSpeedOnIdle = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "ResetAnimatorSpeedOnIdle", true, "Reset animator speed to 1 when the machine returns to idle.アイドル復帰時にAnimator速度を1へ戻す"); DoNotSkipRewardSetState = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "DoNotSkipRewardSetState", true, "Do not fully skip states that confirm rewards.報酬確定Stateを完全スキップしない"); TurboMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Turbo", "TurboMode", true, "Force more aggressive speed values even when old config values remain.既存Config値が残っていてもさらに高速化"); AllowQueuedMultiUse = ((BaseUnityPlugin)this).Config.Bind<bool>("MultiUse", "AllowQueuedMultiUse", true, "Allow multiple players to queue gacha uses while the machine is busy.ガチャ機が使用中でも複数人の利用要求をキュー化"); QueueLocalPresses = ((BaseUnityPlugin)this).Config.Bind<bool>("MultiUse", "QueueLocalPresses", true, "Queue local E presses while the machine is busy.ガチャ機が使用中でもローカルのE入力をキュー化"); QueueRemoteRequests = ((BaseUnityPlugin)this).Config.Bind<bool>("MultiUse", "QueueRemoteRequests", true, "Host queues remote player requests received while the machine is busy.ホストが使用中に受信した参加者の要求をキュー化"); MaxQueueSize = ((BaseUnityPlugin)this).Config.Bind<int>("MultiUse", "MaxQueueSize", 32, new ConfigDescription("Maximum total queued gacha requests.全体の最大キュー数", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 200), Array.Empty<object>())); MaxQueuedRequestsPerPlayer = ((BaseUnityPlugin)this).Config.Bind<int>("MultiUse", "MaxQueuedRequestsPerPlayer", 10, new ConfigDescription("Maximum queued gacha requests per player.プレイヤーごとの最大キュー数", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); QueueProcessDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("MultiUse", "QueueProcessDelaySeconds", 0.02f, new ConfigDescription("Delay before starting the next queued gacha after Idle.アイドル復帰後に次のキューを開始するまでの秒数", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); QueueInteractDistance = ((BaseUnityPlugin)this).Config.Bind<float>("MultiUse", "QueueInteractDistance", 5f, new ConfigDescription("Maximum distance for local rapid E queue input.ローカルE連打キュー入力を許可する距離", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>())); AllowRapidPress = ((BaseUnityPlugin)this).Config.Bind<bool>("RapidInput", "AllowRapidPress", true, "Allow repeated E presses to queue consecutive gacha uses. E連打で連続ガチャをキュー化"); RapidPressCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("RapidInput", "RapidPressCooldownSeconds", 0.03f, new ConfigDescription("Minimum interval between rapid E queue inputs. E連打キュー入力の最小間隔", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); } 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<QueuedGachaRequest> PendingRequests = new List<QueuedGachaRequest>(); 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); } } } }