using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using TMPro; using UnityEngine; using UnityEngine.AI; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Codex")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("4.2.2.0")] [assembly: AssemblyInformationalVersion("4.2.2")] [assembly: AssemblyProduct("StagePhysicsEvents")] [assembly: AssemblyTitle("StagePhysicsEvents")] [assembly: AssemblyVersion("4.2.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace REPOJP.StagePhysicsEvents { [BepInPlugin("REPOJP.StagePhysicsEvents", "Stage Flux", "4.2.2")] public sealed class StagePhysicsEventsPlugin : BaseUnityPlugin { public const string PluginGuid = "REPOJP.StagePhysicsEvents"; public const string PluginName = "Stage Flux"; public const string PluginVersion = "4.2.2"; private Harmony? _harmony; private GameObject? _controllerObject; internal static StagePhysicsEventsPlugin Instance { get; private set; } internal static ManualLogSource ModLogger { get; private set; } internal StagePhysicsConfig Settings { get; private set; } internal StagePhysicsEventController Controller { get; private set; } private void Awake() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown Instance = this; ModLogger = ((BaseUnityPlugin)this).Logger; Settings = new StagePhysicsConfig(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); _controllerObject = new GameObject("StagePhysicsEvents_Controller"); ((Object)_controllerObject).hideFlags = (HideFlags)61; _controllerObject.transform.SetParent(((Component)this).transform, false); _controllerObject.SetActive(false); Controller = _controllerObject.AddComponent(); Controller.Initialize(Settings); ((Component)this).gameObject.AddComponent().Initialize(Settings, Controller); _harmony = new Harmony("REPOJP.StagePhysicsEvents"); try { _harmony.PatchAll(typeof(LifecyclePatches)); _harmony.PatchAll(typeof(NotificationEnemyReactionPatches)); _harmony.PatchAll(typeof(EnemySpeedPatches)); SceneManager.activeSceneChanged += ActiveSceneChanged; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Stage Flux 4.2.2 loaded."); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to apply Harmony patches: {arg}"); _harmony.UnpatchSelf(); ((Behaviour)Controller).enabled = false; } } private void OnDestroy() { SceneManager.activeSceneChanged -= ActiveSceneChanged; Controller?.Shutdown(); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } PhysGrabObjectRegistry.Clear(); if ((Object)(object)_controllerObject != (Object)null) { Object.Destroy((Object)(object)_controllerObject); } } private void ActiveSceneChanged(Scene previous, Scene current) { Controller?.StageEnding(); } } internal sealed class StagePhysicsConfig { [Flags] private enum EffectRisk { None = 0, ForcedMovement = 1, LethalHazard = 2, AreaHazard = 4, EnemyPressure = 8, ValuableRisk = 0x10, ControlImpairment = 0x20, ValueLossAmplifier = 0x40, VisualImpairment = 0x80 } private const int CurrentConfigRevision = 9; private static readonly PropertyInfo? OrphanedEntriesProperty = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic); private readonly ManualLogSource _logger; private string _lastProbabilityWarning = string.Empty; private string _lastRangeWarning = string.Empty; internal ConfigEntry Enabled { get; } internal ConfigEntry Mode { get; } internal ConfigEntry StageActivationChancePercent { get; } internal ConfigEntry MaxSimultaneousEffects { get; } internal ConfigEntry AllowDangerousCombinations { get; } internal ConfigEntry ValuableProtectionReleaseDelaySeconds { get; } internal ConfigEntry IntervalMinSeconds { get; } internal ConfigEntry IntervalMaxSeconds { get; } internal ConfigEntry EffectDurationMinSeconds { get; } internal ConfigEntry EffectDurationMaxSeconds { get; } internal ConfigEntry FeatherEnabled { get; } internal ConfigEntry FeatherChance { get; } internal ConfigEntry ZeroGravityEnabled { get; } internal ConfigEntry ZeroGravityChance { get; } internal ConfigEntry ZeroGravityProtectValuables { get; } internal ConfigEntry BatteryEnabled { get; } internal ConfigEntry BatteryChance { get; } internal ConfigEntry BatteryChargeAmount { get; } internal ConfigEntry BatteryChargeIntervalSeconds { get; } internal ConfigEntry HealEnabled { get; } internal ConfigEntry HealChance { get; } internal ConfigEntry HealAmount { get; } internal ConfigEntry HealIntervalSeconds { get; } internal ConfigEntry IndestructibleEnabled { get; } internal ConfigEntry IndestructibleChance { get; } internal ConfigEntry FragilityEnabled { get; } internal ConfigEntry FragilityChance { get; } internal ConfigEntry FragilityMultiplierPercent { get; } internal ConfigEntry GumballHypnosisEnabled { get; } internal ConfigEntry GumballHypnosisChance { get; } internal ConfigEntry HealingAuraEnabled { get; } internal ConfigEntry HealingAuraChance { get; } internal ConfigEntry HealingAuraHealthPool { get; } internal ConfigEntry HealingAuraSpawnCountMin { get; } internal ConfigEntry HealingAuraSpawnCountMax { get; } internal ConfigEntry HealingAuraSpawnIntervalSeconds { get; } internal ConfigEntry HealingAuraMinimumPlayerDistance { get; } internal ConfigEntry HealingAuraMaximumActiveInstances { get; } internal ConfigEntry StarBarrageEnabled { get; } internal ConfigEntry StarBarrageChance { get; } internal ConfigEntry StarBarrageProtectValuables { get; } internal ConfigEntry StarBarrageProjectileCountMin { get; } internal ConfigEntry StarBarrageProjectileCountMax { get; } internal ConfigEntry StarBarrageSpawnIntervalSeconds { get; } internal ConfigEntry StarBarrageMinimumPlayerDistance { get; } internal ConfigEntry StarBarrageMaximumActiveInstances { get; } internal ConfigEntry SpiderScareEnabled { get; } internal ConfigEntry SpiderScareChance { get; } internal ConfigEntry SpiderScarePlayersPerWaveMin { get; } internal ConfigEntry SpiderScarePlayersPerWaveMax { get; } internal ConfigEntry SpiderScareSpawnIntervalSeconds { get; } internal ConfigEntry TrafficShockEnabled { get; } internal ConfigEntry TrafficShockChance { get; } internal ConfigEntry TrafficShockPlayersPerPulseMin { get; } internal ConfigEntry TrafficShockPlayersPerPulseMax { get; } internal ConfigEntry TrafficShockPulseIntervalSeconds { get; } internal ConfigEntry DangerousValuablesEnabled { get; } internal ConfigEntry DangerousValuablesChance { get; } internal ConfigEntry DangerousValuablesProtectValuables { get; } internal ConfigEntry DangerousValuablesIceSawEnabled { get; } internal ConfigEntry DangerousValuablesBlenderEnabled { get; } internal ConfigEntry DangerousValuablesFlamethrowerEnabled { get; } internal ConfigEntry DangerousValuablesEggEnabled { get; } internal ConfigEntry DangerousValuablesCarEnabled { get; } internal ConfigEntry DangerousValuablesPlaneEnabled { get; } internal ConfigEntry DangerousValuablesBroomEnabled { get; } internal ConfigEntry DangerousValuablesActivationCountMin { get; } internal ConfigEntry DangerousValuablesActivationCountMax { get; } internal ConfigEntry DangerousValuablesReactivationIntervalSeconds { get; } internal ConfigEntry DangerousValuablesMaximumActiveInstances { get; } internal ConfigEntry RollEnabled { get; } internal ConfigEntry RollChance { get; } internal ConfigEntry RollProtectValuables { get; } internal ConfigEntry VoidEnabled { get; } internal ConfigEntry VoidChance { get; } internal ConfigEntry VoidProtectValuables { get; } internal ConfigEntry VoidSpawnCountMin { get; } internal ConfigEntry VoidSpawnCountMax { get; } internal ConfigEntry VoidSpawnIntervalSeconds { get; } internal ConfigEntry LevitationEnabled { get; } internal ConfigEntry LevitationChance { get; } internal ConfigEntry LevitationProtectValuables { get; } internal ConfigEntry LevitationSpawnCountMin { get; } internal ConfigEntry LevitationSpawnCountMax { get; } internal ConfigEntry LevitationSpawnIntervalSeconds { get; } internal ConfigEntry LevitationMinimumPlayerDistance { get; } internal ConfigEntry LevitationMaximumActiveInstances { get; } internal ConfigEntry ShockwaveEnabled { get; } internal ConfigEntry ShockwaveChance { get; } internal ConfigEntry ShockwaveProtectValuables { get; } internal ConfigEntry ShockwaveSpawnCountMin { get; } internal ConfigEntry ShockwaveSpawnCountMax { get; } internal ConfigEntry ShockwaveSpawnIntervalSeconds { get; } internal ConfigEntry ShockwaveMinimumPlayerDistance { get; } internal ConfigEntry ShockwaveMaximumActiveInstances { get; } internal ConfigEntry ShockwaveLaunchForceMin { get; } internal ConfigEntry ShockwaveLaunchForceMax { get; } internal ConfigEntry StunBlastEnabled { get; } internal ConfigEntry StunBlastChance { get; } internal ConfigEntry StunBlastProtectValuables { get; } internal ConfigEntry StunBlastSpawnCountMin { get; } internal ConfigEntry StunBlastSpawnCountMax { get; } internal ConfigEntry StunBlastSpawnIntervalSeconds { get; } internal ConfigEntry StunBlastMinimumPlayerDistance { get; } internal ConfigEntry StunBlastMaximumActiveInstances { get; } internal ConfigEntry StunBlastLaunchForceMin { get; } internal ConfigEntry StunBlastLaunchForceMax { get; } internal ConfigEntry ExplosionRainEnabled { get; } internal ConfigEntry ExplosionRainChance { get; } internal ConfigEntry ExplosionRainProtectValuables { get; } internal ConfigEntry ExplosionRainSpawnCountMin { get; } internal ConfigEntry ExplosionRainSpawnCountMax { get; } internal ConfigEntry ExplosionRainSpawnIntervalSeconds { get; } internal ConfigEntry ExplosionRainMinimumPlayerDistance { get; } internal ConfigEntry ExplosionRainMaximumActiveInstances { get; } internal ConfigEntry ExplosionRainLaunchForceMin { get; } internal ConfigEntry ExplosionRainLaunchForceMax { get; } internal ConfigEntry EnemyWaveEnabled { get; } internal ConfigEntry EnemyWaveChance { get; } internal ConfigEntry EnemyWaveSpawnCountMin { get; } internal ConfigEntry EnemyWaveSpawnCountMax { get; } internal ConfigEntry EnemyWaveReplenishIntervalSeconds { get; } internal ConfigEntry EnemyWaveMinimumPlayerDistance { get; } internal ConfigEntry EnemyWaveDespawnOnEnd { get; } internal ConfigEntry MinefieldEnabled { get; } internal ConfigEntry MinefieldChance { get; } internal ConfigEntry MinefieldProtectValuables { get; } internal ConfigEntry MinefieldExplosiveEnabled { get; } internal ConfigEntry MinefieldShockwaveEnabled { get; } internal ConfigEntry MinefieldStunEnabled { get; } internal ConfigEntry MinefieldSpawnCountMin { get; } internal ConfigEntry MinefieldSpawnCountMax { get; } internal ConfigEntry MinefieldReplenishIntervalSeconds { get; } internal ConfigEntry MinefieldMaximumActiveMines { get; } internal ConfigEntry MinefieldMinimumPlayerDistance { get; } internal ConfigEntry MinefieldReplenishTriggeredMines { get; } internal ConfigEntry FreezeEnabled { get; } internal ConfigEntry FreezeChance { get; } internal ConfigEntry StunEnabled { get; } internal ConfigEntry StunChance { get; } internal ConfigEntry EnemyWarpEnabled { get; } internal ConfigEntry EnemyWarpChance { get; } internal ConfigEntry EnemyWarpIntervalSeconds { get; } internal ConfigEntry EnemyWarpEnemiesPerPulse { get; } internal ConfigEntry EnemyWarpMinimumPlayerDistance { get; } internal ConfigEntry EnemyHuntEnabled { get; } internal ConfigEntry EnemyHuntChance { get; } internal ConfigEntry EnemyHuntRetargetIntervalSeconds { get; } internal ConfigEntry EnemySpeedUpEnabled { get; } internal ConfigEntry EnemySpeedUpChance { get; } internal ConfigEntry EnemySpeedUpPercent { get; } internal ConfigEntry EnemySpeedDownEnabled { get; } internal ConfigEntry EnemySpeedDownChance { get; } internal ConfigEntry EnemySpeedDownPercent { get; } internal ConfigEntry EnemyRegenEnabled { get; } internal ConfigEntry EnemyRegenChance { get; } internal ConfigEntry EnemyRegenHealAmount { get; } internal ConfigEntry EnemyRegenIntervalSeconds { get; } internal ConfigEntry EnemyPurgeEnabled { get; } internal ConfigEntry EnemyPurgeChance { get; } internal ConfigEntry EnemyPurgeDamageAmount { get; } internal ConfigEntry EnemyPurgeIntervalSeconds { get; } internal ConfigEntry EnemyPurgeCanKill { get; } internal ConfigEntry DamagePulseEnabled { get; } internal ConfigEntry DamagePulseChance { get; } internal ConfigEntry DamagePulseAmount { get; } internal ConfigEntry DamagePulseIntervalSeconds { get; } internal ConfigEntry DamagePulseSavingGrace { get; } internal ConfigEntry SecondChanceEnabled { get; } internal ConfigEntry SecondChanceChance { get; } internal ConfigEntry SecondChanceCheckIntervalSeconds { get; } internal ConfigEntry SecondChanceMaxRevivesPerPlayer { get; } internal ConfigEntry KnockbackEnabled { get; } internal ConfigEntry KnockbackChance { get; } internal ConfigEntry KnockbackIntervalSeconds { get; } internal ConfigEntry KnockbackHorizontalForce { get; } internal ConfigEntry KnockbackVerticalForce { get; } internal ConfigEntry FlickerEnabled { get; } internal ConfigEntry FlickerChance { get; } internal ConfigEntry FlickerIntervalSeconds { get; } internal ConfigEntry FlickerIntensityPercent { get; } internal ConfigEntry QuakeEnabled { get; } internal ConfigEntry QuakeChance { get; } internal ConfigEntry QuakeProtectValuables { get; } internal ConfigEntry QuakeIntervalSeconds { get; } internal ConfigEntry QuakeForce { get; } internal ConfigEntry DoorChaosEnabled { get; } internal ConfigEntry DoorChaosChance { get; } internal ConfigEntry DoorChaosIntervalSeconds { get; } internal ConfigEntry DoorChaosAffectedPercent { get; } internal ConfigEntry DoorChaosForce { get; } internal ConfigEntry DoorChaosHingedItemsEnabled { get; } internal ConfigEntry ValueSurgeEnabled { get; } internal ConfigEntry ValueSurgeChance { get; } internal ConfigEntry ValueSurgeMultiplierPercent { get; } internal ConfigEntry ValueSurgeRestoreOnEnd { get; } internal ConfigEntry ValueCrashEnabled { get; } internal ConfigEntry ValueCrashChance { get; } internal ConfigEntry ValueCrashMultiplierPercent { get; } internal ConfigEntry ValueCrashRestoreOnEnd { get; } internal ConfigEntry TargetValuables { get; } internal ConfigEntry TargetCosmeticBoxes { get; } internal ConfigEntry TargetItems { get; } internal ConfigEntry TargetDoors { get; } internal ConfigEntry TargetWeapons { get; } internal ConfigEntry TargetPlayers { get; } internal ConfigEntry TargetEnemies { get; } internal ConfigEntry HudEnabled { get; } internal ConfigEntry ChatAnnouncementsEnabled { get; } internal ConfigEntry EnemyReactionEnabled { get; } internal ConfigEntry StartCountdownEnabled { get; } internal ConfigEntry EndCountdownEnabled { get; } internal ConfigEntry HudStyle { get; } internal ConfigEntry HudLayoutDirection { get; } internal ConfigEntry HudAnchor { get; } internal ConfigEntry HudAlignment { get; } internal ConfigEntry HudOffsetX { get; } internal ConfigEntry HudOffsetY { get; } internal ConfigEntry HudScalePercent { get; } internal ConfigEntry HudBackgroundOpacityPercent { get; } internal (int Min, int Max) EffectiveIntervalRange => OrderedRange(IntervalMinSeconds.Value, IntervalMaxSeconds.Value, "Interval"); internal (int Min, int Max) EffectiveDurationRange => OrderedRange(EffectDurationMinSeconds.Value, EffectDurationMaxSeconds.Value, "EffectDuration"); internal bool HasSelectableEffect { get { foreach (StageEffect individualEffect in StageEffectSet.IndividualEffects) { if (GetChance(individualEffect) > 0) { return true; } } return false; } } internal bool MinefieldHasEnabledType { get { if (!MinefieldExplosiveEnabled.Value && !MinefieldShockwaveEnabled.Value) { return MinefieldStunEnabled.Value; } return true; } } internal bool HasGumballTargets { get { if (!TargetValuables.Value && !TargetCosmeticBoxes.Value && !TargetItems.Value) { return TargetWeapons.Value; } return true; } } internal bool DangerousValuablesHasEnabledType { get { if (!DangerousValuablesIceSawEnabled.Value && !DangerousValuablesBlenderEnabled.Value && !DangerousValuablesFlamethrowerEnabled.Value && !DangerousValuablesEggEnabled.Value && !DangerousValuablesCarEnabled.Value && !DangerousValuablesPlaneEnabled.Value) { return DangerousValuablesBroomEnabled.Value; } return true; } } internal StagePhysicsConfig(ConfigFile config, ManualLogSource logger) { //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Expected O, but got Unknown //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Expected O, but got Unknown //IL_2077: Unknown result type (might be due to invalid IL or missing references) //IL_2081: Expected O, but got Unknown //IL_20c0: Unknown result type (might be due to invalid IL or missing references) //IL_20ca: Expected O, but got Unknown //IL_2276: Unknown result type (might be due to invalid IL or missing references) //IL_2280: Expected O, but got Unknown //IL_22bb: Unknown result type (might be due to invalid IL or missing references) //IL_22c5: Expected O, but got Unknown //IL_233b: Unknown result type (might be due to invalid IL or missing references) //IL_2345: Expected O, but got Unknown //IL_238a: Unknown result type (might be due to invalid IL or missing references) //IL_2394: Expected O, but got Unknown //IL_255f: Unknown result type (might be due to invalid IL or missing references) //IL_2569: Expected O, but got Unknown //IL_2571: Unknown result type (might be due to invalid IL or missing references) //IL_257b: Expected O, but got Unknown //IL_2587: Unknown result type (might be due to invalid IL or missing references) //IL_2591: Expected O, but got Unknown //IL_259d: Unknown result type (might be due to invalid IL or missing references) //IL_25a7: Expected O, but got Unknown //IL_25b3: Unknown result type (might be due to invalid IL or missing references) //IL_25bd: Expected O, but got Unknown //IL_25c9: Unknown result type (might be due to invalid IL or missing references) //IL_25d3: Expected O, but got Unknown //IL_25df: Unknown result type (might be due to invalid IL or missing references) //IL_25e9: Expected O, but got Unknown //IL_25f5: Unknown result type (might be due to invalid IL or missing references) //IL_25ff: Expected O, but got Unknown //IL_260b: Unknown result type (might be due to invalid IL or missing references) //IL_2615: Expected O, but got Unknown //IL_2621: Unknown result type (might be due to invalid IL or missing references) //IL_262b: Expected O, but got Unknown //IL_2637: Unknown result type (might be due to invalid IL or missing references) //IL_2641: Expected O, but got Unknown //IL_264d: Unknown result type (might be due to invalid IL or missing references) //IL_2657: Expected O, but got Unknown //IL_2663: Unknown result type (might be due to invalid IL or missing references) //IL_266d: Expected O, but got Unknown //IL_2679: Unknown result type (might be due to invalid IL or missing references) //IL_2683: Expected O, but got Unknown //IL_268f: Unknown result type (might be due to invalid IL or missing references) //IL_2699: Expected O, but got Unknown //IL_26a5: Unknown result type (might be due to invalid IL or missing references) //IL_26af: Expected O, but got Unknown //IL_26bb: Unknown result type (might be due to invalid IL or missing references) //IL_26c5: Expected O, but got Unknown //IL_26d1: Unknown result type (might be due to invalid IL or missing references) //IL_26db: Expected O, but got Unknown //IL_26e7: Unknown result type (might be due to invalid IL or missing references) //IL_26f1: Expected O, but got Unknown //IL_26fd: Unknown result type (might be due to invalid IL or missing references) //IL_2707: Expected O, but got Unknown //IL_2746: Unknown result type (might be due to invalid IL or missing references) //IL_2750: Expected O, but got Unknown //IL_277e: Unknown result type (might be due to invalid IL or missing references) //IL_2788: Expected O, but got Unknown //IL_2794: Unknown result type (might be due to invalid IL or missing references) //IL_279e: Expected O, but got Unknown //IL_27aa: Unknown result type (might be due to invalid IL or missing references) //IL_27b4: Expected O, but got Unknown //IL_27c0: Unknown result type (might be due to invalid IL or missing references) //IL_27ca: Expected O, but got Unknown //IL_27d6: Unknown result type (might be due to invalid IL or missing references) //IL_27e0: Expected O, but got Unknown //IL_27ec: Unknown result type (might be due to invalid IL or missing references) //IL_27f6: Expected O, but got Unknown //IL_2802: Unknown result type (might be due to invalid IL or missing references) //IL_280c: Expected O, but got Unknown //IL_2818: Unknown result type (might be due to invalid IL or missing references) //IL_2822: Expected O, but got Unknown //IL_282e: Unknown result type (might be due to invalid IL or missing references) //IL_2838: Expected O, but got Unknown //IL_2844: Unknown result type (might be due to invalid IL or missing references) //IL_284e: Expected O, but got Unknown //IL_285a: Unknown result type (might be due to invalid IL or missing references) //IL_2864: Expected O, but got Unknown //IL_2870: Unknown result type (might be due to invalid IL or missing references) //IL_287a: Expected O, but got Unknown //IL_2886: Unknown result type (might be due to invalid IL or missing references) //IL_2890: Expected O, but got Unknown //IL_289c: Unknown result type (might be due to invalid IL or missing references) //IL_28a6: Expected O, but got Unknown //IL_28b2: Unknown result type (might be due to invalid IL or missing references) //IL_28bc: Expected O, but got Unknown //IL_28c8: Unknown result type (might be due to invalid IL or missing references) //IL_28d2: Expected O, but got Unknown //IL_28de: Unknown result type (might be due to invalid IL or missing references) //IL_28e8: Expected O, but got Unknown //IL_28f4: Unknown result type (might be due to invalid IL or missing references) //IL_28fe: Expected O, but got Unknown //IL_290a: Unknown result type (might be due to invalid IL or missing references) //IL_2914: Expected O, but got Unknown //IL_2931: Unknown result type (might be due to invalid IL or missing references) //IL_293b: Expected O, but got Unknown //IL_2947: Unknown result type (might be due to invalid IL or missing references) //IL_2951: Expected O, but got Unknown //IL_295d: Unknown result type (might be due to invalid IL or missing references) //IL_2967: Expected O, but got Unknown //IL_2973: Unknown result type (might be due to invalid IL or missing references) //IL_297d: Expected O, but got Unknown //IL_2989: Unknown result type (might be due to invalid IL or missing references) //IL_2993: Expected O, but got Unknown //IL_299f: Unknown result type (might be due to invalid IL or missing references) //IL_29a9: Expected O, but got Unknown //IL_29b5: Unknown result type (might be due to invalid IL or missing references) //IL_29bf: Expected O, but got Unknown //IL_29cb: Unknown result type (might be due to invalid IL or missing references) //IL_29d5: Expected O, but got Unknown //IL_29e1: Unknown result type (might be due to invalid IL or missing references) //IL_29eb: Expected O, but got Unknown //IL_29f7: Unknown result type (might be due to invalid IL or missing references) //IL_2a01: Expected O, but got Unknown //IL_2a0d: Unknown result type (might be due to invalid IL or missing references) //IL_2a17: Expected O, but got Unknown //IL_2a23: Unknown result type (might be due to invalid IL or missing references) //IL_2a2d: Expected O, but got Unknown //IL_2a39: Unknown result type (might be due to invalid IL or missing references) //IL_2a43: Expected O, but got Unknown //IL_2a4f: Unknown result type (might be due to invalid IL or missing references) //IL_2a59: Expected O, but got Unknown //IL_2a65: Unknown result type (might be due to invalid IL or missing references) //IL_2a6f: Expected O, but got Unknown //IL_2a7b: Unknown result type (might be due to invalid IL or missing references) //IL_2a85: Expected O, but got Unknown _logger = logger; bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; string text = "Light" + "weight"; ConfigEntry val = BindBool(config, text, "Enabled", value: true, "Legacy setting migrated to Feather."); ConfigEntry val2 = BindInt(config, text, "ChancePercent", 5, 0, 100, "Legacy setting migrated to Feather."); BindBool(config, "Magnet", "Enabled", value: true, "Obsolete setting removed in 4.1.0."); BindInt(config, "Magnet", "ChancePercent", 10, 0, 100, "Obsolete setting removed in 4.1.0."); ConfigEntry val3 = BindBool(config, "Torque", "Enabled", value: false, "Legacy Torque setting migrated to Roll."); ConfigEntry val4 = BindInt(config, "Torque", "ChancePercent", 5, 0, 100, "Legacy Torque setting migrated to Roll."); ConfigEntry val5 = BindBool(config, "Roll Staff", "Enabled", val3.Value, "Legacy Roll Staff setting migrated to Roll."); ConfigEntry val6 = BindInt(config, "Roll Staff", "ChancePercent", val4.Value, 0, 100, "Legacy Roll Staff setting migrated to Roll."); BindBool(config, "Boost", "Enabled", value: true, "Obsolete setting removed in 4.1.0."); BindInt(config, "Boost", "ChancePercent", 3, 0, 100, "Obsolete setting removed in 4.1.0."); ConfigEntry val7 = BindInt(config, "General", "StageEffectChancePercent", 20, 0, 100, "Legacy setting migrated to StageActivationChancePercent."); Enabled = BindBool(config, "General", "Enabled", value: true, "Enables the mod. Stage event behavior uses the host's settings."); Mode = config.Bind("General", "Mode", "RandomEachEvent", new ConfigDescription("AllMode selects one of the other four modes with equal probability when each stage begins. RandomEachEvent rerolls every event. FixedForStage keeps the stage's first effect combination, duration, and interval. FixedPerExtraction keeps the stage's duration and interval but rerolls the effect combination after each completed extraction. PersistentForStage keeps the selected effect combination active until the stage ends.", (AcceptableValueBase)(object)new AcceptableValueList(new string[5] { "AllMode", "RandomEachEvent", "FixedForStage", "FixedPerExtraction", "PersistentForStage" }), Array.Empty())); StageActivationChancePercent = BindInt(config, "General", "StageActivationChancePercent", val7.Value, 0, 100, "Chance that stage events are enabled for a stage. A failed roll disables effects, notifications, and the HUD for that stage."); MaxSimultaneousEffects = BindInt(config, "General", "MaxSimultaneousEffects", 3, 1, 5, "Maximum number of effects that can be selected for one event. Successful effects above this limit are reduced randomly."); AllowDangerousCombinations = BindBool(config, "Safety", "AllowDangerousCombinations", value: false, "Allows combinations that can greatly increase player death, uncontrolled movement, enemy pressure, visual disruption, or valuable loss."); ConfigEntry val8 = BindInt(config, "Safety", "HazardIndestructibleReleaseDelaySeconds", 2, 1, 5, "Legacy setting migrated to ValuableProtectionReleaseDelaySeconds."); ValuableProtectionReleaseDelaySeconds = BindInt(config, "Safety", "ValuableProtectionReleaseDelaySeconds", val8.Value, 1, 5, "Seconds to keep targeted valuables Indestructible after a hazard event ends."); EffectDurationMinSeconds = BindInt(config, "Timing", "EffectDurationMinSeconds", 15, 10, 300, "Minimum effect duration in seconds."); EffectDurationMaxSeconds = BindInt(config, "Timing", "EffectDurationMaxSeconds", 30, 10, 300, "Maximum effect duration in seconds."); IntervalMinSeconds = BindInt(config, "Timing", "IntervalMinSeconds", 45, 10, 300, "Minimum number of seconds before the next event roll."); IntervalMaxSeconds = BindInt(config, "Timing", "IntervalMaxSeconds", 90, 10, 300, "Maximum number of seconds before the next event roll."); FeatherEnabled = BindBool(config, "Feather", "Enabled", val.Value, "Includes Feather in each event's independent rolls."); FeatherChance = BindInt(config, "Feather", "ChancePercent", val2.Value, 0, 100, "Independent chance for Feather in each event."); ConfigDefinition definition = new ConfigDefinition("Internal", "ConfigRevision"); int num = ReadConfigRevision(config, definition); if (num < 1 && FeatherChance.Value == 30) { FeatherChance.Value = 15; _logger.LogInfo((object)"Migrated the previous default Feather chance from 30% to 15%."); } ZeroGravityEnabled = BindBool(config, "Zero Gravity", "Enabled", value: true, "Includes Zero Gravity in each event's independent rolls."); ZeroGravityChance = BindInt(config, "Zero Gravity", "ChancePercent", 5, 0, 100, "Independent chance for Zero Gravity in each event."); ZeroGravityProtectValuables = BindBool(config, "Zero Gravity", "ProtectValuables", value: true, "Temporarily protects targeted valuables from damage caused by Zero Gravity."); int val9 = ((num < 9) ? BindInt(config, "Zero Gravity", "IndestructibleReleaseDelaySeconds", 2, 1, 5, "Legacy setting migrated to Safety.ValuableProtectionReleaseDelaySeconds.").Value : 2); ConfigEntry val10 = BindBool(config, "Battery", "Enabled", value: true, "Legacy setting migrated to Battery Charge."); ConfigEntry val11 = BindInt(config, "Battery", "ChancePercent", 5, 0, 100, "Legacy setting migrated to Battery Charge."); ConfigEntry val12 = BindInt(config, "Battery", "ChargeAmount", 5, 1, 100, "Legacy setting migrated to Battery Charge."); ConfigEntry val13 = BindInt(config, "Battery", "ChargeIntervalSeconds", 4, 1, 300, "Legacy setting migrated to Battery Charge."); if (num < 2) { if (val12.Value == 10) { val12.Value = 5; _logger.LogInfo((object)"Migrated the previous default Battery charge amount from 10 to 5."); } if (val13.Value == 2) { val13.Value = 4; _logger.LogInfo((object)"Migrated the previous default Battery charge interval from 2 seconds to 4 seconds."); } } ConfigEntry val14 = BindBool(config, "BatteryCharge", "Enabled", val10.Value, "Legacy setting migrated to Battery Charge."); ConfigEntry val15 = BindInt(config, "BatteryCharge", "ChancePercent", val11.Value, 0, 100, "Legacy setting migrated to Battery Charge."); ConfigEntry val16 = BindInt(config, "BatteryCharge", "ChargeAmount", val12.Value, 1, 100, "Legacy setting migrated to Battery Charge."); ConfigEntry val17 = BindInt(config, "BatteryCharge", "ChargeIntervalSeconds", val13.Value, 1, 300, "Legacy setting migrated to Battery Charge."); BatteryEnabled = BindBool(config, "Battery Charge", "Enabled", val14.Value, "Includes Battery Charge in each event's independent rolls."); BatteryChance = BindInt(config, "Battery Charge", "ChancePercent", val15.Value, 0, 100, "Independent chance for Battery Charge in each event."); BatteryChargeAmount = BindInt(config, "Battery Charge", "ChargeAmount", val16.Value, 1, 100, "Battery percentage points restored to each targeted battery per charge tick."); BatteryChargeIntervalSeconds = BindInt(config, "Battery Charge", "ChargeIntervalSeconds", val17.Value, 1, 300, "Seconds between Battery Charge ticks."); HealEnabled = BindBool(config, "Heal", "Enabled", value: true, "Includes the Heal Orb effect in each event's independent rolls."); HealChance = BindInt(config, "Heal", "ChancePercent", 5, 0, 100, "Independent chance for Heal in each event."); HealAmount = BindInt(config, "Heal", "HealAmount", 10, 1, 100, "Health restored to each targeted player per Heal tick."); HealIntervalSeconds = BindInt(config, "Heal", "HealIntervalSeconds", 2, 1, 300, "Seconds between Heal ticks."); IndestructibleEnabled = BindBool(config, "Indestructible", "Enabled", value: true, "Includes the Indestructible Orb effect in each event's independent rolls."); IndestructibleChance = BindInt(config, "Indestructible", "ChancePercent", 5, 0, 100, "Independent chance for Indestructible in each event."); FragilityEnabled = BindBool(config, "Fragility", "Enabled", value: true, "Includes the stage-wide valuable Fragility effect in each event's independent rolls."); FragilityChance = BindInt(config, "Fragility", "ChancePercent", 5, 0, 100, "Independent chance for Fragility in each event."); FragilityMultiplierPercent = BindInt(config, "Fragility", "FragilityMultiplierPercent", 1000, 101, 5000, "Valuable impact-fragility multiplier. 100 is vanilla; this event accepts only values above 100 so valuables always become easier to break."); if (num < 3 && FragilityMultiplierPercent.Value == 200) { FragilityMultiplierPercent.Value = 1000; _logger.LogInfo((object)"Migrated the previous default Fragility multiplier from 200% to 1000%."); } GumballHypnosisEnabled = BindBool(config, "Gumball Hypnosis", "Enabled", value: true, "Includes Gumball Hypnosis in each event's independent rolls."); GumballHypnosisChance = BindInt(config, "Gumball Hypnosis", "ChancePercent", 5, 0, 100, "Independent chance for Gumball Hypnosis. Holding an enabled object category applies the vanilla Gumball screen and gaze effect until it is released."); HealingAuraEnabled = BindBool(config, "Healing Aura", "Enabled", value: true, "Includes Healing Aura in each event's independent rolls."); HealingAuraChance = BindInt(config, "Healing Aura", "ChancePercent", 5, 0, 100, "Independent chance for Healing Aura."); HealingAuraHealthPool = BindInt(config, "Healing Aura", "HealthPool", 50, 1, 1000, "Total health available from each spawned Healing Aura."); HealingAuraSpawnCountMin = BindInt(config, "Healing Aura", "SpawnCountMin", 1, 1, 30, "Minimum number of Healing Auras created in each wave."); HealingAuraSpawnCountMax = BindInt(config, "Healing Aura", "SpawnCountMax", 3, 1, 30, "Maximum number of Healing Auras created in each wave."); HealingAuraMaximumActiveInstances = BindInt(config, "Healing Aura", "MaximumActiveInstances", 30, 1, 30, "Maximum number of tracked Healing Auras."); HealingAuraSpawnIntervalSeconds = BindInt(config, "Healing Aura", "SpawnIntervalSeconds", 10, 1, 300, "Seconds between Healing Aura spawn waves."); HealingAuraMinimumPlayerDistance = BindInt(config, "Healing Aura", "MinimumPlayerDistance", 0, 0, 100, "Minimum distance from a living player when selecting a Healing Aura spawn point."); StarBarrageEnabled = BindBool(config, "Star Barrage", "Enabled", value: true, "Includes Star Barrage in each event's independent rolls."); StarBarrageChance = BindInt(config, "Star Barrage", "ChancePercent", 5, 0, 100, "Independent chance for Star Barrage."); StarBarrageProjectileCountMin = BindInt(config, "Star Barrage", "ProjectileCountMin", 3, 1, 30, "Minimum number of Star Wand projectiles launched in each wave."); StarBarrageProjectileCountMax = BindInt(config, "Star Barrage", "ProjectileCountMax", 6, 1, 30, "Maximum number of Star Wand projectiles launched in each wave."); StarBarrageMaximumActiveInstances = BindInt(config, "Star Barrage", "MaximumActiveInstances", 30, 1, 30, "Maximum number of tracked Star Wand projectiles."); StarBarrageSpawnIntervalSeconds = BindInt(config, "Star Barrage", "SpawnIntervalSeconds", 2, 1, 300, "Seconds between Star Barrage waves."); StarBarrageMinimumPlayerDistance = BindInt(config, "Star Barrage", "MinimumPlayerDistance", 5, 0, 100, "Minimum distance from a living player when selecting a projectile origin."); StarBarrageProtectValuables = BindBool(config, "Star Barrage", "ProtectValuables", value: true, "Temporarily protects targeted valuables from damage caused by Star Barrage."); SpiderScareEnabled = BindBool(config, "Spider Scare", "Enabled", value: true, "Includes Spider Scare in each event's independent rolls."); SpiderScareChance = BindInt(config, "Spider Scare", "ChancePercent", 5, 0, 100, "Independent chance for Spider Scare."); SpiderScarePlayersPerWaveMin = BindInt(config, "Spider Scare", "PlayersPerWaveMin", 1, 1, 30, "Minimum number of random living player positions targeted in each wave."); SpiderScarePlayersPerWaveMax = BindInt(config, "Spider Scare", "PlayersPerWaveMax", 3, 1, 30, "Maximum number of random living player positions targeted in each wave."); SpiderScareSpawnIntervalSeconds = BindInt(config, "Spider Scare", "SpawnIntervalSeconds", 10, 1, 300, "Seconds between Spider Scare waves."); TrafficShockEnabled = BindBool(config, "Traffic Shock", "Enabled", value: true, "Includes Traffic Shock in each event's independent rolls."); TrafficShockChance = BindInt(config, "Traffic Shock", "ChancePercent", 5, 0, 100, "Independent chance for Traffic Shock."); TrafficShockPlayersPerPulseMin = BindInt(config, "Traffic Shock", "PlayersPerPulseMin", 1, 1, 30, "Minimum number of random living players shocked in each pulse."); TrafficShockPlayersPerPulseMax = BindInt(config, "Traffic Shock", "PlayersPerPulseMax", 3, 1, 30, "Maximum number of random living players shocked in each pulse."); TrafficShockPulseIntervalSeconds = BindInt(config, "Traffic Shock", "PulseIntervalSeconds", 10, 1, 300, "Seconds between Traffic Shock pulses."); DangerousValuablesEnabled = BindBool(config, "Dangerous Valuables", "Enabled", value: true, "Includes Dangerous Valuables in each event's independent rolls."); DangerousValuablesChance = BindInt(config, "Dangerous Valuables", "ChancePercent", 5, 0, 100, "Independent chance for Dangerous Valuables."); ConfigEntry val18 = BindInt(config, "Dangerous Valuables", "SpawnCountMin", 5, 1, 30, "Legacy setting migrated to ActivationCountMin."); ConfigEntry val19 = BindInt(config, "Dangerous Valuables", "SpawnCountMax", 10, 1, 30, "Legacy setting migrated to ActivationCountMax."); if (num < 5) { MigrateSpawnCountDefaults(val18, val19, 1, 3, 5, 10, "Dangerous Valuables"); } ConfigEntry val20 = BindInt(config, "Dangerous Valuables", "ReplenishIntervalSeconds", 10, 1, 300, "Legacy setting migrated to ReactivationIntervalSeconds."); BindInt(config, "Dangerous Valuables", "MinimumPlayerDistance", 5, 0, 100, "Obsolete spawn setting."); BindBool(config, "Dangerous Valuables", "ReplenishDestroyed", value: true, "Obsolete spawn setting."); DangerousValuablesActivationCountMin = BindInt(config, "Dangerous Valuables", "ActivationCountMin", val18.Value, 1, 30, "Minimum number of already-placed dangerous valuables activated."); DangerousValuablesActivationCountMax = BindInt(config, "Dangerous Valuables", "ActivationCountMax", val19.Value, 1, 30, "Maximum number of already-placed dangerous valuables activated."); DangerousValuablesMaximumActiveInstances = BindInt(config, "Dangerous Valuables", "MaximumActiveInstances", 15, 1, 30, "Maximum number of tracked dangerous valuables."); DangerousValuablesReactivationIntervalSeconds = BindInt(config, "Dangerous Valuables", "ReactivationIntervalSeconds", val20.Value, 1, 300, "Seconds between reactivation checks for the selected existing valuables."); DangerousValuablesBlenderEnabled = BindBool(config, "Dangerous Valuables", "BlenderEnabled", value: true, "Allows already-placed Blender valuables to be activated."); DangerousValuablesBroomEnabled = BindBool(config, "Dangerous Valuables", "BroomEnabled", value: true, "Allows already-placed Broom valuables to be activated."); DangerousValuablesCarEnabled = BindBool(config, "Dangerous Valuables", "CarEnabled", value: true, "Allows already-placed Car valuables to be activated."); DangerousValuablesEggEnabled = BindBool(config, "Dangerous Valuables", "EggEnabled", value: true, "Allows already-placed Egg valuables to be activated."); DangerousValuablesFlamethrowerEnabled = BindBool(config, "Dangerous Valuables", "FlamethrowerEnabled", value: true, "Allows already-placed Flamethrower valuables to be activated."); DangerousValuablesIceSawEnabled = BindBool(config, "Dangerous Valuables", "IceSawEnabled", value: true, "Allows already-placed Ice Saw valuables to be activated."); DangerousValuablesPlaneEnabled = BindBool(config, "Dangerous Valuables", "PlaneEnabled", value: true, "Allows already-placed Plane valuables to be activated."); DangerousValuablesProtectValuables = BindBool(config, "Dangerous Valuables", "ProtectValuables", value: false, "Temporarily protects targeted valuables from damage caused by Dangerous Valuables."); RollEnabled = BindBool(config, "Roll", "Enabled", val5.Value, "Includes Roll in each event's independent rolls."); RollChance = BindInt(config, "Roll", "ChancePercent", val6.Value, 0, 100, "Independent chance for Roll in each event."); RollProtectValuables = BindBool(config, "Roll", "ProtectValuables", value: true, "Temporarily protects targeted valuables from damage caused by Roll."); int val21 = ((num < 9) ? BindInt(config, "Roll", "IndestructibleReleaseDelaySeconds", 2, 1, 5, "Legacy setting migrated to Safety.ValuableProtectionReleaseDelaySeconds.").Value : 2); VoidEnabled = BindBool(config, "Void", "Enabled", value: false, "Includes the Void Staff effect in each event's independent rolls."); VoidChance = BindInt(config, "Void", "ChancePercent", 5, 0, 100, "Independent chance for Void in each event."); int val22 = ((num < 9) ? BindInt(config, "Void", "IndestructibleReleaseDelaySeconds", 2, 1, 5, "Legacy setting migrated to Safety.ValuableProtectionReleaseDelaySeconds.").Value : 2); VoidSpawnCountMin = BindInt(config, "Void", "SpawnCountMin", 3, 2, 30, "Minimum number of Void effects created at random stage points."); VoidSpawnCountMax = BindInt(config, "Void", "SpawnCountMax", 5, 2, 30, "Maximum number of Void effects created at random stage points."); VoidSpawnIntervalSeconds = BindInt(config, "Void", "SpawnIntervalSeconds", 10, 1, 300, "Seconds between Void effect regeneration waves."); VoidProtectValuables = BindBool(config, "Void", "ProtectValuables", value: true, "Temporarily protects targeted valuables from damage caused by Void."); LevitationEnabled = BindBool(config, "Levitation", "Enabled", value: true, "Includes Levitation in each event's independent rolls."); LevitationChance = BindInt(config, "Levitation", "ChancePercent", 5, 0, 100, "Independent chance for Levitation in each event."); LevitationSpawnCountMin = BindInt(config, "Levitation", "SpawnCountMin", 2, 1, 30, "Minimum number of Levitation effects created in each wave."); LevitationSpawnCountMax = BindInt(config, "Levitation", "SpawnCountMax", 5, 1, 30, "Maximum number of Levitation effects created in each wave."); LevitationMaximumActiveInstances = BindInt(config, "Levitation", "MaximumActiveInstances", 30, 1, 30, "Maximum number of tracked Levitation source instances."); LevitationSpawnIntervalSeconds = BindInt(config, "Levitation", "SpawnIntervalSeconds", 10, 1, 300, "Seconds between Levitation spawn waves."); LevitationMinimumPlayerDistance = BindInt(config, "Levitation", "MinimumPlayerDistance", 3, 0, 100, "Minimum distance from a living player when selecting a Levitation spawn point."); LevitationProtectValuables = BindBool(config, "Levitation", "ProtectValuables", value: true, "Temporarily protects targeted valuables from damage caused by Levitation."); ShockwaveEnabled = BindBool(config, "Shockwave", "Enabled", value: true, "Includes Shockwave in each event's independent rolls."); ShockwaveChance = BindInt(config, "Shockwave", "ChancePercent", 5, 0, 100, "Independent chance for Shockwave in each event."); ShockwaveSpawnCountMin = BindInt(config, "Shockwave", "SpawnCountMin", 5, 1, 30, "Minimum number of Shockwave grenades created in each wave."); ShockwaveSpawnCountMax = BindInt(config, "Shockwave", "SpawnCountMax", 10, 1, 30, "Maximum number of Shockwave grenades created in each wave."); ShockwaveMaximumActiveInstances = BindInt(config, "Shockwave", "MaximumActiveInstances", 30, 1, 30, "Maximum number of tracked Shockwave grenades."); ShockwaveSpawnIntervalSeconds = BindInt(config, "Shockwave", "SpawnIntervalSeconds", 10, 1, 300, "Seconds between Shockwave spawn waves."); ShockwaveMinimumPlayerDistance = BindInt(config, "Shockwave", "MinimumPlayerDistance", 5, 0, 100, "Minimum distance from a living player when selecting a Shockwave spawn point."); ShockwaveLaunchForceMin = BindInt(config, "Shockwave", "LaunchForceMin", 6, 0, 100, "Minimum velocity-change force used to launch each Shockwave grenade in a random upward direction."); ShockwaveLaunchForceMax = BindInt(config, "Shockwave", "LaunchForceMax", 12, 0, 100, "Maximum velocity-change force used to launch each Shockwave grenade in a random upward direction."); ShockwaveProtectValuables = BindBool(config, "Shockwave", "ProtectValuables", value: true, "Temporarily protects targeted valuables from damage caused by Shockwave."); StunBlastEnabled = BindBool(config, "Stun Blast", "Enabled", value: true, "Includes Stun Blast in each event's independent rolls."); StunBlastChance = BindInt(config, "Stun Blast", "ChancePercent", 5, 0, 100, "Independent chance for Stun Blast in each event."); StunBlastSpawnCountMin = BindInt(config, "Stun Blast", "SpawnCountMin", 5, 1, 30, "Minimum number of Stun grenades created in each wave."); StunBlastSpawnCountMax = BindInt(config, "Stun Blast", "SpawnCountMax", 10, 1, 30, "Maximum number of Stun grenades created in each wave."); StunBlastMaximumActiveInstances = BindInt(config, "Stun Blast", "MaximumActiveInstances", 30, 1, 30, "Maximum number of tracked Stun grenades."); StunBlastSpawnIntervalSeconds = BindInt(config, "Stun Blast", "SpawnIntervalSeconds", 10, 1, 300, "Seconds between Stun Blast spawn waves."); StunBlastMinimumPlayerDistance = BindInt(config, "Stun Blast", "MinimumPlayerDistance", 5, 0, 100, "Minimum distance from a living player when selecting a Stun Blast spawn point."); StunBlastLaunchForceMin = BindInt(config, "Stun Blast", "LaunchForceMin", 6, 0, 100, "Minimum velocity-change force used to launch each Stun grenade in a random upward direction."); StunBlastLaunchForceMax = BindInt(config, "Stun Blast", "LaunchForceMax", 12, 0, 100, "Maximum velocity-change force used to launch each Stun grenade in a random upward direction."); StunBlastProtectValuables = BindBool(config, "Stun Blast", "ProtectValuables", value: true, "Temporarily protects targeted valuables from damage caused by Stun Blast."); ExplosionRainEnabled = BindBool(config, "Explosion Rain", "Enabled", value: true, "Includes Explosion Rain in each event's independent rolls."); ExplosionRainChance = BindInt(config, "Explosion Rain", "ChancePercent", 5, 0, 100, "Independent chance for Explosion Rain in each event."); ExplosionRainSpawnCountMin = BindInt(config, "Explosion Rain", "SpawnCountMin", 5, 1, 30, "Minimum number of explosive grenades created in each wave."); ExplosionRainSpawnCountMax = BindInt(config, "Explosion Rain", "SpawnCountMax", 10, 1, 30, "Maximum number of explosive grenades created in each wave."); ExplosionRainMaximumActiveInstances = BindInt(config, "Explosion Rain", "MaximumActiveInstances", 30, 1, 30, "Maximum number of tracked explosive grenades."); ExplosionRainSpawnIntervalSeconds = BindInt(config, "Explosion Rain", "SpawnIntervalSeconds", 10, 1, 300, "Seconds between Explosion Rain spawn waves."); ExplosionRainMinimumPlayerDistance = BindInt(config, "Explosion Rain", "MinimumPlayerDistance", 10, 0, 100, "Minimum distance from a living player when selecting an Explosion Rain spawn point."); ExplosionRainLaunchForceMin = BindInt(config, "Explosion Rain", "LaunchForceMin", 6, 0, 100, "Minimum velocity-change force used to launch each explosive grenade in a random upward direction."); ExplosionRainLaunchForceMax = BindInt(config, "Explosion Rain", "LaunchForceMax", 12, 0, 100, "Maximum velocity-change force used to launch each explosive grenade in a random upward direction."); ExplosionRainProtectValuables = BindBool(config, "Explosion Rain", "ProtectValuables", value: true, "Temporarily protects targeted valuables from damage caused by Explosion Rain."); EnemyWaveEnabled = BindBool(config, "Enemy Wave", "Enabled", value: true, "Includes Enemy Wave in each event's independent rolls."); EnemyWaveChance = BindInt(config, "Enemy Wave", "ChancePercent", 5, 0, 100, "Independent chance for Enemy Wave in each event."); EnemyWaveSpawnCountMin = BindInt(config, "Enemy Wave", "SpawnCountMin", 1, 1, 30, "Minimum number of additional enemies kept active."); EnemyWaveSpawnCountMax = BindInt(config, "Enemy Wave", "SpawnCountMax", 3, 1, 30, "Maximum number of additional enemies kept active."); EnemyWaveMinimumPlayerDistance = BindInt(config, "Enemy Wave", "MinimumPlayerDistance", 10, 0, 100, "Minimum requested distance from players when placing an additional enemy."); EnemyWaveReplenishIntervalSeconds = BindInt(config, "Enemy Wave", "ReplenishIntervalSeconds", 10, 1, 300, "Seconds between checks that replenish missing wave enemies."); EnemyWaveDespawnOnEnd = BindBool(config, "Enemy Wave", "DespawnOnEnd", value: true, "Despawns surviving enemies activated by the event when it ends."); MinefieldEnabled = BindBool(config, "Minefield", "Enabled", value: true, "Includes Minefield in each event's independent rolls."); MinefieldChance = BindInt(config, "Minefield", "ChancePercent", 5, 0, 100, "Independent chance for Minefield in each event."); MinefieldExplosiveEnabled = BindBool(config, "Minefield", "ExplosiveEnabled", value: true, "Allows explosive mines in Minefield."); MinefieldShockwaveEnabled = BindBool(config, "Minefield", "ShockwaveEnabled", value: true, "Allows shockwave mines in Minefield."); MinefieldStunEnabled = BindBool(config, "Minefield", "StunEnabled", value: true, "Allows stun mines in Minefield."); MinefieldSpawnCountMin = BindInt(config, "Minefield", "SpawnCountMin", 10, 1, 30, "Minimum number of armed mines kept active."); MinefieldSpawnCountMax = BindInt(config, "Minefield", "SpawnCountMax", 15, 1, 30, "Maximum number of armed mines kept active."); ConfigEntry val23 = BindInt(config, "Minefield", "SpawnIntervalSeconds", 10, 1, 300, "Legacy setting migrated to ReplenishIntervalSeconds."); MinefieldMaximumActiveMines = BindInt(config, "Minefield", "MaximumActiveMines", 30, 1, 30, "Maximum number of event-created mines that may remain active."); MinefieldReplenishTriggeredMines = BindBool(config, "Minefield", "ReplenishTriggeredMines", value: true, "Replenishes triggered or destroyed event mines while Minefield remains active."); MinefieldReplenishIntervalSeconds = BindInt(config, "Minefield", "ReplenishIntervalSeconds", val23.Value, 1, 300, "Seconds between Minefield replenishment checks."); MinefieldMinimumPlayerDistance = BindInt(config, "Minefield", "MinimumPlayerDistance", 8, 0, 100, "Minimum distance from a living player when selecting a mine position."); MinefieldProtectValuables = BindBool(config, "Minefield", "ProtectValuables", value: true, "Temporarily protects targeted valuables from damage caused by Minefield."); FreezeEnabled = BindBool(config, "Freeze", "Enabled", value: true, "Includes Freeze in each event's independent rolls."); FreezeChance = BindInt(config, "Freeze", "ChancePercent", 5, 0, 100, "Independent chance for Freeze in each event."); StunEnabled = BindBool(config, "Stun", "Enabled", value: true, "Includes enemy Stun in each event's independent rolls."); StunChance = BindInt(config, "Stun", "ChancePercent", 5, 0, 100, "Independent chance for enemy Stun in each event."); EnemyWarpEnabled = BindBool(config, "Enemy Warp", "Enabled", value: true, "Includes Enemy Warp in each event's independent rolls."); EnemyWarpChance = BindInt(config, "Enemy Warp", "ChancePercent", 5, 0, 100, "Independent chance for Enemy Warp in each event."); EnemyWarpEnemiesPerPulse = BindInt(config, "Enemy Warp", "EnemiesPerPulse", 3, 1, 30, "Maximum enemies teleported by each pulse."); EnemyWarpMinimumPlayerDistance = BindInt(config, "Enemy Warp", "MinimumPlayerDistance", 10, 0, 100, "Minimum requested distance from players for enemy teleport destinations."); EnemyWarpIntervalSeconds = BindInt(config, "Enemy Warp", "IntervalSeconds", 10, 1, 300, "Seconds between Enemy Warp pulses."); EnemyHuntEnabled = BindBool(config, "Enemy Hunt", "Enabled", value: true, "Includes Enemy Hunt in each event's independent rolls."); EnemyHuntChance = BindInt(config, "Enemy Hunt", "ChancePercent", 5, 0, 100, "Independent chance for Enemy Hunt in each event."); EnemyHuntRetargetIntervalSeconds = BindInt(config, "Enemy Hunt", "RetargetIntervalSeconds", 5, 1, 300, "Seconds between synchronized lure sounds in player-occupied rooms after all extractions are complete."); EnemySpeedUpEnabled = BindBool(config, "Enemy Speed Up", "Enabled", value: true, "Includes Enemy Speed Up in each event's independent rolls."); EnemySpeedUpChance = BindInt(config, "Enemy Speed Up", "ChancePercent", 5, 0, 100, "Independent chance for Enemy Speed Up in each event."); EnemySpeedUpPercent = BindInt(config, "Enemy Speed Up", "SpeedPercent", 150, 101, 500, "Enemy navigation speed and acceleration percentage while Enemy Speed Up is active."); EnemySpeedDownEnabled = BindBool(config, "Enemy Speed Down", "Enabled", value: true, "Includes Enemy Speed Down in each event's independent rolls."); EnemySpeedDownChance = BindInt(config, "Enemy Speed Down", "ChancePercent", 5, 0, 100, "Independent chance for Enemy Speed Down in each event."); EnemySpeedDownPercent = BindInt(config, "Enemy Speed Down", "SpeedPercent", 50, 10, 99, "Enemy navigation speed and acceleration percentage while Enemy Speed Down is active."); EnemyRegenEnabled = BindBool(config, "Enemy Regen", "Enabled", value: true, "Includes Enemy Regen in each event's independent rolls."); EnemyRegenChance = BindInt(config, "Enemy Regen", "ChancePercent", 5, 0, 100, "Independent chance for Enemy Regen in each event."); EnemyRegenHealAmount = BindInt(config, "Enemy Regen", "HealAmount", 10, 1, 100, "Health restored to each enemy per pulse."); EnemyRegenIntervalSeconds = BindInt(config, "Enemy Regen", "HealIntervalSeconds", 5, 1, 300, "Seconds between Enemy Regen pulses."); EnemyPurgeEnabled = BindBool(config, "Enemy Purge", "Enabled", value: true, "Includes Enemy Purge in each event's independent rolls."); EnemyPurgeChance = BindInt(config, "Enemy Purge", "ChancePercent", 5, 0, 100, "Independent chance for Enemy Purge in each event."); EnemyPurgeDamageAmount = BindInt(config, "Enemy Purge", "DamageAmount", 10, 1, 1000, "Damage dealt to each enemy per pulse."); EnemyPurgeCanKill = BindBool(config, "Enemy Purge", "CanKill", value: true, "Allows Enemy Purge to reduce an enemy to zero health."); EnemyPurgeIntervalSeconds = BindInt(config, "Enemy Purge", "DamageIntervalSeconds", 5, 1, 300, "Seconds between Enemy Purge pulses."); DamagePulseEnabled = BindBool(config, "Damage Pulse", "Enabled", value: true, "Includes Damage Pulse in each event's independent rolls."); DamagePulseChance = BindInt(config, "Damage Pulse", "ChancePercent", 5, 0, 100, "Independent chance for Damage Pulse in each event."); DamagePulseAmount = BindInt(config, "Damage Pulse", "DamageAmount", 5, 1, 100, "Damage dealt to each living player per pulse."); DamagePulseSavingGrace = BindBool(config, "Damage Pulse", "SavingGrace", value: true, "Prevents Damage Pulse alone from reducing a player below one health."); DamagePulseIntervalSeconds = BindInt(config, "Damage Pulse", "DamageIntervalSeconds", 5, 1, 300, "Seconds between Damage Pulse pulses."); SecondChanceEnabled = BindBool(config, "Second Chance", "Enabled", value: true, "Includes Second Chance in each event's independent rolls."); SecondChanceChance = BindInt(config, "Second Chance", "ChancePercent", 5, 0, 100, "Independent chance for Second Chance in each event."); SecondChanceMaxRevivesPerPlayer = BindInt(config, "Second Chance", "MaxRevivesPerPlayer", 1, 1, 10, "Maximum in-place revives granted to each player during one event."); SecondChanceCheckIntervalSeconds = BindInt(config, "Second Chance", "CheckIntervalSeconds", 2, 1, 300, "Seconds between ordinary death checks. A failed-stage retake is intercepted immediately."); KnockbackEnabled = BindBool(config, "Knockback", "Enabled", value: true, "Includes Knockback in each event's independent rolls."); KnockbackChance = BindInt(config, "Knockback", "ChancePercent", 5, 0, 100, "Independent chance for Knockback in each event."); KnockbackHorizontalForce = BindInt(config, "Knockback", "HorizontalForce", 8, 0, 100, "Horizontal force applied by Knockback."); KnockbackVerticalForce = BindInt(config, "Knockback", "VerticalForce", 3, 0, 100, "Upward force applied by Knockback."); KnockbackIntervalSeconds = BindInt(config, "Knockback", "IntervalSeconds", 5, 1, 300, "Seconds between Knockback pulses."); FlickerEnabled = BindBool(config, "Flicker", "Enabled", value: true, "Includes Flicker in each event's independent rolls."); FlickerChance = BindInt(config, "Flicker", "ChancePercent", 5, 0, 100, "Independent chance for Flicker in each event."); FlickerIntensityPercent = BindInt(config, "Flicker", "IntensityPercent", 200, 1, 500, "Flicker strength percentage. Higher values repeat more visible vanilla light transitions per pulse."); FlickerIntervalSeconds = BindInt(config, "Flicker", "IntervalSeconds", 2, 1, 300, "Seconds between vanilla light flicker pulses."); QuakeEnabled = BindBool(config, "Quake", "Enabled", value: true, "Includes Quake in each event's independent rolls."); QuakeChance = BindInt(config, "Quake", "ChancePercent", 5, 0, 100, "Independent chance for Quake in each event."); QuakeForce = BindInt(config, "Quake", "Force", 8, 0, 100, "Force applied by Quake."); QuakeIntervalSeconds = BindInt(config, "Quake", "IntervalSeconds", 5, 1, 300, "Seconds between Quake pulses."); QuakeProtectValuables = BindBool(config, "Quake", "ProtectValuables", value: true, "Temporarily protects targeted valuables from damage caused by Quake."); DoorChaosEnabled = BindBool(config, "Door Chaos", "Enabled", value: true, "Includes Door Chaos in each event's independent rolls."); DoorChaosChance = BindInt(config, "Door Chaos", "ChancePercent", 5, 0, 100, "Independent chance for Door Chaos in each event."); DoorChaosHingedItemsEnabled = BindBool(config, "Door Chaos", "HingedItemsEnabled", value: true, "Allows lids and other hinged movable items to be affected."); DoorChaosAffectedPercent = BindInt(config, "Door Chaos", "AffectedPercent", 30, 1, 100, "Percentage of eligible hinges changed by each pulse."); DoorChaosForce = BindInt(config, "Door Chaos", "Force", 12, 1, 30, "Physical force used to swing doors and hinged objects open or closed."); DoorChaosIntervalSeconds = BindInt(config, "Door Chaos", "IntervalSeconds", 2, 1, 300, "Seconds between Door Chaos pulses."); BindBool(config, "Trap Frenzy", "Enabled", value: false, "Obsolete setting removed in 4.2.0."); BindInt(config, "Trap Frenzy", "ChancePercent", 5, 0, 100, "Obsolete setting removed in 4.2.0."); BindInt(config, "Trap Frenzy", "IntervalSeconds", 10, 1, 300, "Obsolete setting removed in 4.2.0."); BindInt(config, "Trap Frenzy", "TrapsPerPulse", 3, 1, 30, "Obsolete setting removed in 4.2.0."); BindBool(config, "Trap Frenzy", "IncludeExplosiveTraps", value: false, "Obsolete setting removed in 4.2.0."); BindBool(config, "Object Shuffle", "Enabled", value: false, "Obsolete setting removed in 4.2.0."); BindInt(config, "Object Shuffle", "ChancePercent", 5, 0, 100, "Obsolete setting removed in 4.2.0."); BindInt(config, "Object Shuffle", "IntervalSeconds", 10, 1, 300, "Obsolete setting removed in 4.2.0."); BindInt(config, "Object Shuffle", "ObjectsPerPulse", 3, 1, 30, "Obsolete setting removed in 4.2.0."); BindInt(config, "Object Shuffle", "MinimumPlayerDistance", 5, 0, 100, "Obsolete setting removed in 4.2.0."); ValueSurgeEnabled = BindBool(config, "Value Surge", "Enabled", value: true, "Includes Value Surge in each event's independent rolls."); ValueSurgeChance = BindInt(config, "Value Surge", "ChancePercent", 5, 0, 100, "Independent chance for Value Surge in each event."); ValueSurgeMultiplierPercent = BindInt(config, "Value Surge", "MultiplierPercent", 150, 1, 1000, "Temporary multiplier applied to all active valuable prices, including extraction, cart, and truck areas."); ValueSurgeRestoreOnEnd = BindBool(config, "Value Surge", "RestoreOnEnd", value: true, "Restores surviving valuables to their saved prices when the event ends."); ValueCrashEnabled = BindBool(config, "Value Crash", "Enabled", value: true, "Includes Value Crash in each event's independent rolls."); ValueCrashChance = BindInt(config, "Value Crash", "ChancePercent", 5, 0, 100, "Independent chance for Value Crash in each event."); ValueCrashMultiplierPercent = BindInt(config, "Value Crash", "MultiplierPercent", 50, 1, 1000, "Temporary multiplier applied to all active valuable prices, including extraction, cart, and truck areas."); ValueCrashRestoreOnEnd = BindBool(config, "Value Crash", "RestoreOnEnd", value: true, "Restores surviving valuables to their saved prices when the event ends."); BindBool(config, "Music Override", "Enabled", value: false, "Obsolete setting removed in 4.2.0."); BindInt(config, "Music Override", "ChancePercent", 5, 0, 100, "Obsolete setting removed in 4.2.0."); config.Bind("Music Override", "TrackSelection", "Random", "Obsolete setting removed in 4.2.0."); BindBool(config, "Music Override", "RestoreOnEnd", value: true, "Obsolete setting removed in 4.2.0."); ConfigEntry val24 = BindBool(config, "What floats", "Valuables", value: true, "Legacy target setting."); ConfigEntry val25 = BindBool(config, "What floats", "Items", value: true, "Legacy target setting."); ConfigEntry val26 = BindBool(config, "What floats", "Doors", value: false, "Legacy target setting."); ConfigEntry val27 = BindBool(config, "What floats", "Weapons", value: true, "Legacy target setting."); ConfigEntry val28 = BindBool(config, "What floats", "Players", value: true, "Legacy target setting."); ConfigEntry val29 = BindBool(config, "What floats", "Enemies", value: true, "Legacy target setting."); TargetPlayers = BindBool(config, "Targets", "Players", val28.Value, "Allows applicable events to target players."); TargetEnemies = BindBool(config, "Targets", "Enemies", val29.Value, "Allows applicable events to target enemies."); TargetValuables = BindBool(config, "Targets", "Valuables", val24.Value, "Allows applicable events to target valuables and enables automatic valuable protection."); TargetCosmeticBoxes = BindBool(config, "Targets", "CosmeticBoxes", value: false, "Allows applicable physics events to target Cosmetic Boxes independently from general items."); TargetItems = BindBool(config, "Targets", "Items", val25.Value, "Allows applicable events to target general items, carts, and movable props."); TargetWeapons = BindBool(config, "Targets", "Weapons", val27.Value, "Allows applicable events to target weapons."); TargetDoors = BindBool(config, "Targets", "Doors", val26.Value, "Allows applicable physics events to target doors, lids, and other hinged objects."); ConfigEntry val30 = BindBool(config, "UI", "Enabled", value: true, "Legacy HUD setting."); ConfigEntry val31 = config.Bind("UI", "Anchor", "BottomRight", new ConfigDescription("Legacy HUD setting.", (AcceptableValueBase)(object)new AcceptableValueList(new string[9] { "TopLeft", "TopCenter", "TopRight", "MiddleLeft", "MiddleCenter", "MiddleRight", "BottomLeft", "BottomCenter", "BottomRight" }), Array.Empty())); ConfigEntry val32 = config.Bind("UI", "Alignment", "Right", new ConfigDescription("Legacy HUD setting.", (AcceptableValueBase)(object)new AcceptableValueList(new string[3] { "Left", "Center", "Right" }), Array.Empty())); ConfigEntry val33 = BindInt(config, "UI", "OffsetX", 0, -3840, 3840, "Legacy HUD setting."); ConfigEntry val34 = BindInt(config, "UI", "OffsetY", 0, -2160, 2160, "Legacy HUD setting."); ConfigEntry val35 = BindInt(config, "UI", "ScalePercent", 70, 50, 200, "Legacy HUD setting."); ConfigEntry val36 = BindBool(config, "UI", "StageStartChatEnabled", value: true, "Legacy notification setting."); ConfigEntry val37 = BindBool(config, "UI", "CountdownEnabled", value: true, "Legacy countdown setting migrated to the separate start and end settings."); ConfigEntry val38 = BindBool(config, "UI", "StartCountdownEnabled", val37.Value, "Legacy notification setting."); ConfigEntry val39 = BindBool(config, "UI", "EndCountdownEnabled", val37.Value, "Legacy notification setting."); ChatAnnouncementsEnabled = BindBool(config, "Notifications", "ChatAnnouncementsEnabled", val36.Value, "Broadcasts the stage summary, effect names, countdowns, and End through vanilla chat."); StartCountdownEnabled = BindBool(config, "Notifications", "StartCountdownEnabled", val38.Value, "Enables the five-second countdown during the final five seconds of the interval. When disabled, only the effect name is announced three seconds before the interval ends."); EndCountdownEnabled = BindBool(config, "Notifications", "EndCountdownEnabled", val39.Value, "Enables the three-second countdown before an event ends. When disabled, End is announced when the effect ends."); EnemyReactionEnabled = BindBool(config, "Notifications", "EnemyReactionEnabled", value: false, "Allows enemies to investigate sounds generated by Stage Flux chat notifications. Ordinary voice chat and other sounds remain detectable."); HudEnabled = BindBool(config, "HUD", "Enabled", val30.Value, "Shows the synchronized event HUD on this client."); HudStyle = config.Bind("HUD", "Style", "Graphical", new ConfigDescription("Graphical shows the five-slot icon HUD. Classic restores the previous text-only HUD.", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "Graphical", "Classic" }), Array.Empty())); HudLayoutDirection = config.Bind("HUD", "LayoutDirection", "Vertical", new ConfigDescription("Direction used by the Graphical HUD. Slots stop from top to bottom in Vertical mode and from left to right in Horizontal mode.", (AcceptableValueBase)(object)new AcceptableValueList(new string[2] { "Vertical", "Horizontal" }), Array.Empty())); HudAnchor = config.Bind("HUD", "Anchor", val31.Value, new ConfigDescription("Anchor point used to position the HUD.", (AcceptableValueBase)(object)new AcceptableValueList(new string[9] { "TopLeft", "TopCenter", "TopRight", "MiddleLeft", "MiddleCenter", "MiddleRight", "BottomLeft", "BottomCenter", "BottomRight" }), Array.Empty())); HudAlignment = config.Bind("HUD", "Alignment", val32.Value, new ConfigDescription("Horizontal alignment of the HUD text.", (AcceptableValueBase)(object)new AcceptableValueList(new string[3] { "Left", "Center", "Right" }), Array.Empty())); HudOffsetX = BindInt(config, "HUD", "OffsetX", val33.Value, -3840, 3840, "Horizontal offset from the anchor in pixels."); HudOffsetY = BindInt(config, "HUD", "OffsetY", val34.Value, -2160, 2160, "Vertical offset from the anchor in pixels."); HudScalePercent = BindInt(config, "HUD", "ScalePercent", val35.Value, 50, 200, "HUD scale percentage."); HudBackgroundOpacityPercent = BindInt(config, "HUD", "BackgroundOpacityPercent", 50, 0, 100, "Graphical HUD background opacity percentage. HUD icons and text remain fully visible."); if (num < 4) { MigrateSpawnCountDefaults(ShockwaveSpawnCountMin, ShockwaveSpawnCountMax, 1, 3, 5, 10, "Shockwave"); MigrateSpawnCountDefaults(StunBlastSpawnCountMin, StunBlastSpawnCountMax, 1, 3, 5, 10, "Stun Blast"); MigrateSpawnCountDefaults(ExplosionRainSpawnCountMin, ExplosionRainSpawnCountMax, 1, 3, 5, 10, "Explosion Rain"); MigrateSpawnCountDefaults(MinefieldSpawnCountMin, MinefieldSpawnCountMax, 3, 6, 10, 15, "Minefield"); } if (num < 7 && DoorChaosIntervalSeconds.Value == 5) { DoorChaosIntervalSeconds.Value = 2; _logger.LogInfo((object)"Migrated the previous default Door Chaos interval from 5 seconds to 2 seconds."); } if (num < 9) { int num2 = Math.Max(ValuableProtectionReleaseDelaySeconds.Value, Math.Max(val9, Math.Max(val21, val22))); if (ValuableProtectionReleaseDelaySeconds.Value != num2) { ValuableProtectionReleaseDelaySeconds.Value = num2; _logger.LogInfo((object)("Migrated the longest event-specific valuable protection release delay to " + $"Safety.ValuableProtectionReleaseDelaySeconds ({num2} seconds).")); } } if (num < 9) { WriteConfigRevision(config, definition); } config.Remove(new ConfigDefinition(text, "Enabled")); config.Remove(new ConfigDefinition(text, "ChancePercent")); config.Remove(new ConfigDefinition("Magnet", "Enabled")); config.Remove(new ConfigDefinition("Magnet", "ChancePercent")); config.Remove(new ConfigDefinition("Torque", "Enabled")); config.Remove(new ConfigDefinition("Torque", "ChancePercent")); config.Remove(new ConfigDefinition("Roll Staff", "Enabled")); config.Remove(new ConfigDefinition("Roll Staff", "ChancePercent")); config.Remove(new ConfigDefinition("Boost", "Enabled")); config.Remove(new ConfigDefinition("Boost", "ChancePercent")); config.Remove(new ConfigDefinition("Battery", "Enabled")); config.Remove(new ConfigDefinition("Battery", "ChancePercent")); config.Remove(new ConfigDefinition("Battery", "ChargeAmount")); config.Remove(new ConfigDefinition("Battery", "ChargeIntervalSeconds")); config.Remove(new ConfigDefinition("BatteryCharge", "Enabled")); config.Remove(new ConfigDefinition("BatteryCharge", "ChancePercent")); config.Remove(new ConfigDefinition("BatteryCharge", "ChargeAmount")); config.Remove(new ConfigDefinition("BatteryCharge", "ChargeIntervalSeconds")); config.Remove(new ConfigDefinition("General", "StageEffectChancePercent")); config.Remove(new ConfigDefinition("Safety", "HazardIndestructibleReleaseDelaySeconds")); RemoveObsoleteConfigEntry(config, "Zero Gravity", "IndestructibleReleaseDelaySeconds"); RemoveObsoleteConfigEntry(config, "Roll", "IndestructibleReleaseDelaySeconds"); RemoveObsoleteConfigEntry(config, "Void", "IndestructibleReleaseDelaySeconds"); config.Remove(new ConfigDefinition("Minefield", "SpawnIntervalSeconds")); RemoveObsoleteConfigEntry(config, "Minefield", "RemoveOnEventEnd"); RemoveObsoleteConfigEntry(config, "Dangerous Valuables", "RemoveOnEventEnd"); config.Remove(new ConfigDefinition("Dangerous Valuables", "SpawnCountMin")); config.Remove(new ConfigDefinition("Dangerous Valuables", "SpawnCountMax")); config.Remove(new ConfigDefinition("Dangerous Valuables", "ReplenishIntervalSeconds")); config.Remove(new ConfigDefinition("Dangerous Valuables", "MinimumPlayerDistance")); config.Remove(new ConfigDefinition("Dangerous Valuables", "ReplenishDestroyed")); config.Remove(new ConfigDefinition("Trap Frenzy", "Enabled")); config.Remove(new ConfigDefinition("Trap Frenzy", "ChancePercent")); config.Remove(new ConfigDefinition("Trap Frenzy", "IntervalSeconds")); config.Remove(new ConfigDefinition("Trap Frenzy", "TrapsPerPulse")); config.Remove(new ConfigDefinition("Trap Frenzy", "IncludeExplosiveTraps")); config.Remove(new ConfigDefinition("Object Shuffle", "Enabled")); config.Remove(new ConfigDefinition("Object Shuffle", "ChancePercent")); config.Remove(new ConfigDefinition("Object Shuffle", "IntervalSeconds")); config.Remove(new ConfigDefinition("Object Shuffle", "ObjectsPerPulse")); config.Remove(new ConfigDefinition("Object Shuffle", "MinimumPlayerDistance")); config.Remove(new ConfigDefinition("Music Override", "Enabled")); config.Remove(new ConfigDefinition("Music Override", "ChancePercent")); config.Remove(new ConfigDefinition("Music Override", "TrackSelection")); config.Remove(new ConfigDefinition("Music Override", "RestoreOnEnd")); RemoveObsoleteConfigEntry(config, "Door Chaos", "MapDoorsEnabled"); config.Remove(new ConfigDefinition("What floats", "Valuables")); config.Remove(new ConfigDefinition("What floats", "Items")); config.Remove(new ConfigDefinition("What floats", "Doors")); config.Remove(new ConfigDefinition("What floats", "Weapons")); config.Remove(new ConfigDefinition("What floats", "Players")); config.Remove(new ConfigDefinition("What floats", "Enemies")); config.Remove(new ConfigDefinition("UI", "Enabled")); config.Remove(new ConfigDefinition("UI", "StageStartChatEnabled")); config.Remove(new ConfigDefinition("UI", "CountdownEnabled")); config.Remove(new ConfigDefinition("UI", "StartCountdownEnabled")); config.Remove(new ConfigDefinition("UI", "EndCountdownEnabled")); config.Remove(new ConfigDefinition("UI", "Anchor")); config.Remove(new ConfigDefinition("UI", "Alignment")); config.Remove(new ConfigDefinition("UI", "OffsetX")); config.Remove(new ConfigDefinition("UI", "OffsetY")); config.Remove(new ConfigDefinition("UI", "ScalePercent")); config.SaveOnConfigSet = saveOnConfigSet; config.Save(); FeatherEnabled.SettingChanged += ProbabilitySettingChanged; FeatherChance.SettingChanged += ProbabilitySettingChanged; ZeroGravityEnabled.SettingChanged += ProbabilitySettingChanged; ZeroGravityChance.SettingChanged += ProbabilitySettingChanged; BatteryEnabled.SettingChanged += ProbabilitySettingChanged; BatteryChance.SettingChanged += ProbabilitySettingChanged; HealEnabled.SettingChanged += ProbabilitySettingChanged; HealChance.SettingChanged += ProbabilitySettingChanged; IndestructibleEnabled.SettingChanged += ProbabilitySettingChanged; IndestructibleChance.SettingChanged += ProbabilitySettingChanged; FragilityEnabled.SettingChanged += ProbabilitySettingChanged; FragilityChance.SettingChanged += ProbabilitySettingChanged; GumballHypnosisEnabled.SettingChanged += ProbabilitySettingChanged; GumballHypnosisChance.SettingChanged += ProbabilitySettingChanged; HealingAuraEnabled.SettingChanged += ProbabilitySettingChanged; HealingAuraChance.SettingChanged += ProbabilitySettingChanged; StarBarrageEnabled.SettingChanged += ProbabilitySettingChanged; StarBarrageChance.SettingChanged += ProbabilitySettingChanged; SpiderScareEnabled.SettingChanged += ProbabilitySettingChanged; SpiderScareChance.SettingChanged += ProbabilitySettingChanged; TrafficShockEnabled.SettingChanged += ProbabilitySettingChanged; TrafficShockChance.SettingChanged += ProbabilitySettingChanged; DangerousValuablesEnabled.SettingChanged += ProbabilitySettingChanged; DangerousValuablesChance.SettingChanged += ProbabilitySettingChanged; DangerousValuablesIceSawEnabled.SettingChanged += ProbabilitySettingChanged; DangerousValuablesBlenderEnabled.SettingChanged += ProbabilitySettingChanged; DangerousValuablesFlamethrowerEnabled.SettingChanged += ProbabilitySettingChanged; DangerousValuablesEggEnabled.SettingChanged += ProbabilitySettingChanged; DangerousValuablesCarEnabled.SettingChanged += ProbabilitySettingChanged; DangerousValuablesPlaneEnabled.SettingChanged += ProbabilitySettingChanged; DangerousValuablesBroomEnabled.SettingChanged += ProbabilitySettingChanged; RollEnabled.SettingChanged += ProbabilitySettingChanged; RollChance.SettingChanged += ProbabilitySettingChanged; VoidEnabled.SettingChanged += ProbabilitySettingChanged; VoidChance.SettingChanged += ProbabilitySettingChanged; LevitationEnabled.SettingChanged += ProbabilitySettingChanged; LevitationChance.SettingChanged += ProbabilitySettingChanged; ShockwaveEnabled.SettingChanged += ProbabilitySettingChanged; ShockwaveChance.SettingChanged += ProbabilitySettingChanged; StunBlastEnabled.SettingChanged += ProbabilitySettingChanged; StunBlastChance.SettingChanged += ProbabilitySettingChanged; ExplosionRainEnabled.SettingChanged += ProbabilitySettingChanged; ExplosionRainChance.SettingChanged += ProbabilitySettingChanged; EnemyWaveEnabled.SettingChanged += ProbabilitySettingChanged; EnemyWaveChance.SettingChanged += ProbabilitySettingChanged; MinefieldEnabled.SettingChanged += ProbabilitySettingChanged; MinefieldChance.SettingChanged += ProbabilitySettingChanged; MinefieldExplosiveEnabled.SettingChanged += ProbabilitySettingChanged; MinefieldShockwaveEnabled.SettingChanged += ProbabilitySettingChanged; MinefieldStunEnabled.SettingChanged += ProbabilitySettingChanged; FreezeEnabled.SettingChanged += ProbabilitySettingChanged; FreezeChance.SettingChanged += ProbabilitySettingChanged; StunEnabled.SettingChanged += ProbabilitySettingChanged; StunChance.SettingChanged += ProbabilitySettingChanged; EnemyWarpEnabled.SettingChanged += ProbabilitySettingChanged; EnemyWarpChance.SettingChanged += ProbabilitySettingChanged; EnemyHuntEnabled.SettingChanged += ProbabilitySettingChanged; EnemyHuntChance.SettingChanged += ProbabilitySettingChanged; EnemySpeedUpEnabled.SettingChanged += ProbabilitySettingChanged; EnemySpeedUpChance.SettingChanged += ProbabilitySettingChanged; EnemySpeedDownEnabled.SettingChanged += ProbabilitySettingChanged; EnemySpeedDownChance.SettingChanged += ProbabilitySettingChanged; EnemyRegenEnabled.SettingChanged += ProbabilitySettingChanged; EnemyRegenChance.SettingChanged += ProbabilitySettingChanged; EnemyPurgeEnabled.SettingChanged += ProbabilitySettingChanged; EnemyPurgeChance.SettingChanged += ProbabilitySettingChanged; DamagePulseEnabled.SettingChanged += ProbabilitySettingChanged; DamagePulseChance.SettingChanged += ProbabilitySettingChanged; SecondChanceEnabled.SettingChanged += ProbabilitySettingChanged; SecondChanceChance.SettingChanged += ProbabilitySettingChanged; KnockbackEnabled.SettingChanged += ProbabilitySettingChanged; KnockbackChance.SettingChanged += ProbabilitySettingChanged; FlickerEnabled.SettingChanged += ProbabilitySettingChanged; FlickerChance.SettingChanged += ProbabilitySettingChanged; QuakeEnabled.SettingChanged += ProbabilitySettingChanged; QuakeChance.SettingChanged += ProbabilitySettingChanged; DoorChaosEnabled.SettingChanged += ProbabilitySettingChanged; DoorChaosChance.SettingChanged += ProbabilitySettingChanged; ValueSurgeEnabled.SettingChanged += ProbabilitySettingChanged; ValueSurgeChance.SettingChanged += ProbabilitySettingChanged; ValueCrashEnabled.SettingChanged += ProbabilitySettingChanged; ValueCrashChance.SettingChanged += ProbabilitySettingChanged; } internal int RollIntervalSeconds() { var (num, num2) = EffectiveIntervalRange; return Random.Range(num, num2 + 1); } internal int RollDurationSeconds() { var (num, num2) = EffectiveDurationRange; return Random.Range(num, num2 + 1); } internal int RollVoidSpawnCount() { var (num, num2) = OrderedRange(VoidSpawnCountMin.Value, VoidSpawnCountMax.Value, "VoidSpawnCount"); return Random.Range(num, num2 + 1); } internal int RollLevitationSpawnCount() { return RollConfiguredCount(LevitationSpawnCountMin, LevitationSpawnCountMax, "LevitationSpawnCount"); } internal int RollShockwaveSpawnCount() { return RollConfiguredCount(ShockwaveSpawnCountMin, ShockwaveSpawnCountMax, "ShockwaveSpawnCount"); } internal int RollStunBlastSpawnCount() { return RollConfiguredCount(StunBlastSpawnCountMin, StunBlastSpawnCountMax, "StunBlastSpawnCount"); } internal int RollExplosionRainSpawnCount() { return RollConfiguredCount(ExplosionRainSpawnCountMin, ExplosionRainSpawnCountMax, "ExplosionRainSpawnCount"); } internal int RollEnemyWaveSpawnCount() { return RollConfiguredCount(EnemyWaveSpawnCountMin, EnemyWaveSpawnCountMax, "EnemyWaveSpawnCount"); } internal int RollMinefieldSpawnCount() { return RollConfiguredCount(MinefieldSpawnCountMin, MinefieldSpawnCountMax, "MinefieldSpawnCount"); } internal int RollHealingAuraSpawnCount() { return RollConfiguredCount(HealingAuraSpawnCountMin, HealingAuraSpawnCountMax, "HealingAuraSpawnCount"); } internal int RollStarBarrageProjectileCount() { return RollConfiguredCount(StarBarrageProjectileCountMin, StarBarrageProjectileCountMax, "StarBarrageProjectileCount"); } internal int RollSpiderScarePlayerCount() { return RollConfiguredCount(SpiderScarePlayersPerWaveMin, SpiderScarePlayersPerWaveMax, "SpiderScarePlayersPerWave"); } internal int RollTrafficShockPlayerCount() { return RollConfiguredCount(TrafficShockPlayersPerPulseMin, TrafficShockPlayersPerPulseMax, "TrafficShockPlayersPerPulse"); } internal int RollDangerousValuableActivationCount() { return RollConfiguredCount(DangerousValuablesActivationCountMin, DangerousValuablesActivationCountMax, "DangerousValuablesActivationCount"); } internal StageEffect RollEffects(int? maximumOverride = null) { List list = new List(); foreach (StageEffect individualEffect in StageEffectSet.IndividualEffects) { int chance = GetChance(individualEffect); if (chance >= 100 || (chance > 0 && Random.Range(1, 101) <= chance)) { list.Add(individualEffect); } } Shuffle(list); List list2 = new List(); foreach (StageEffect item in list) { if (HasLogicalConflict(item, list2)) { _logger.LogDebug((object)$"Discarding {item} because it conflicts with an already selected effect."); } else if (!AllowDangerousCombinations.Value && HasDangerousConflict(item, list2)) { _logger.LogDebug((object)$"Discarding {item} because dangerous combinations are disabled."); } else { list2.Add(item); } } int num = Mathf.Clamp(maximumOverride ?? MaxSimultaneousEffects.Value, 1, 5); if (list2.Count > num) { _logger.LogDebug((object)$"Effect roll produced {list2.Count} compatible effects; randomly limiting the event to {num}."); } StageEffect stageEffect = StageEffect.None; int num2 = Mathf.Min(list2.Count, num); for (int i = 0; i < num2; i++) { stageEffect |= list2[i]; } return stageEffect; } internal bool RollStageEnabled() { if (StageActivationChancePercent.Value < 100) { if (StageActivationChancePercent.Value > 0) { return Random.Range(1, 101) <= StageActivationChancePercent.Value; } return false; } return true; } internal bool IsEffectSelectable(StageEffect effect) { return GetChance(effect) > 0; } internal EventMode CaptureMode() { if (string.Equals(Mode.Value, "AllMode", StringComparison.Ordinal)) { return Random.Range(0, 4) switch { 1 => EventMode.FixedForStage, 2 => EventMode.FixedPerExtraction, 3 => EventMode.PersistentForStage, _ => EventMode.RandomEachEvent, }; } if (string.Equals(Mode.Value, "FixedForStage", StringComparison.Ordinal)) { return EventMode.FixedForStage; } if (string.Equals(Mode.Value, "FixedPerExtraction", StringComparison.Ordinal)) { return EventMode.FixedPerExtraction; } if (string.Equals(Mode.Value, "PersistentForStage", StringComparison.Ordinal)) { return EventMode.PersistentForStage; } return EventMode.RandomEachEvent; } internal void LogProbabilityPolicy() { if (!HasSelectableEffect) { if (_lastProbabilityWarning != "zero") { _lastProbabilityWarning = "zero"; _logger.LogWarning((object)"All enabled effect probabilities are 0%; no stage event can be selected."); } } else { _lastProbabilityWarning = string.Empty; } } internal TargetFilter CaptureTargetFilter() { return new TargetFilter(TargetValuables.Value, TargetCosmeticBoxes.Value, TargetItems.Value, TargetDoors.Value, TargetWeapons.Value, TargetPlayers.Value, TargetEnemies.Value); } private void ProbabilitySettingChanged(object sender, EventArgs eventArgs) { LogProbabilityPolicy(); } private int GetChance(StageEffect effect) { if (effect <= StageEffect.EnemyPurge) { if (effect <= StageEffect.StunBlast) { if (effect <= StageEffect.Roll) { if (effect <= StageEffect.Heal) { StageEffect num = effect - 1; if ((ulong)num <= 3uL) { switch (num) { case StageEffect.None: return FeatherEnabled.Value ? FeatherChance.Value : 0; case StageEffect.Feather: return ZeroGravityEnabled.Value ? ZeroGravityChance.Value : 0; case StageEffect.Feather | StageEffect.ZeroGravity: return BatteryEnabled.Value ? BatteryChance.Value : 0; case StageEffect.ZeroGravity: goto IL_076a; } } if (effect == StageEffect.Heal) { return HealEnabled.Value ? HealChance.Value : 0; } } else { switch (effect) { case StageEffect.Indestructible: return IndestructibleEnabled.Value ? IndestructibleChance.Value : 0; case StageEffect.Roll: return RollEnabled.Value ? RollChance.Value : 0; } } } else { switch (effect) { case StageEffect.Void: return VoidEnabled.Value ? VoidChance.Value : 0; case StageEffect.Levitation: return LevitationEnabled.Value ? LevitationChance.Value : 0; case StageEffect.Shockwave: return ShockwaveEnabled.Value ? ShockwaveChance.Value : 0; case StageEffect.StunBlast: return StunBlastEnabled.Value ? StunBlastChance.Value : 0; } } } else { switch (effect) { case StageEffect.ExplosionRain: return ExplosionRainEnabled.Value ? ExplosionRainChance.Value : 0; case StageEffect.EnemyWave: return EnemyWaveEnabled.Value ? EnemyWaveChance.Value : 0; case StageEffect.Minefield: return (MinefieldEnabled.Value && MinefieldHasEnabledType) ? MinefieldChance.Value : 0; case StageEffect.Freeze: return FreezeEnabled.Value ? FreezeChance.Value : 0; case StageEffect.Stun: return StunEnabled.Value ? StunChance.Value : 0; case StageEffect.EnemyWarp: return EnemyWarpEnabled.Value ? EnemyWarpChance.Value : 0; case StageEffect.EnemyHunt: return EnemyHuntEnabled.Value ? EnemyHuntChance.Value : 0; case StageEffect.EnemyRegen: return EnemyRegenEnabled.Value ? EnemyRegenChance.Value : 0; case StageEffect.EnemyPurge: return EnemyPurgeEnabled.Value ? EnemyPurgeChance.Value : 0; } } } else { switch (effect) { case StageEffect.Fragility: return (FragilityEnabled.Value && TargetValuables.Value) ? FragilityChance.Value : 0; case StageEffect.GumballHypnosis: return (GumballHypnosisEnabled.Value && HasGumballTargets) ? GumballHypnosisChance.Value : 0; case StageEffect.HealingAura: return (HealingAuraEnabled.Value && TargetPlayers.Value) ? HealingAuraChance.Value : 0; case StageEffect.StarBarrage: return StarBarrageEnabled.Value ? StarBarrageChance.Value : 0; case StageEffect.SpiderScare: return (SpiderScareEnabled.Value && TargetPlayers.Value) ? SpiderScareChance.Value : 0; case StageEffect.TrafficShock: return (TrafficShockEnabled.Value && TargetPlayers.Value) ? TrafficShockChance.Value : 0; case StageEffect.DangerousValuables: return (DangerousValuablesEnabled.Value && DangerousValuablesHasEnabledType) ? DangerousValuablesChance.Value : 0; case StageEffect.EnemySpeedUp: return EnemySpeedUpEnabled.Value ? EnemySpeedUpChance.Value : 0; case StageEffect.EnemySpeedDown: return EnemySpeedDownEnabled.Value ? EnemySpeedDownChance.Value : 0; case StageEffect.DamagePulse: return DamagePulseEnabled.Value ? DamagePulseChance.Value : 0; case StageEffect.SecondChance: return SecondChanceEnabled.Value ? SecondChanceChance.Value : 0; case StageEffect.Knockback: return KnockbackEnabled.Value ? KnockbackChance.Value : 0; case StageEffect.Flicker: return FlickerEnabled.Value ? FlickerChance.Value : 0; case StageEffect.Quake: return QuakeEnabled.Value ? QuakeChance.Value : 0; case StageEffect.DoorChaos: return DoorChaosEnabled.Value ? DoorChaosChance.Value : 0; case StageEffect.ValueSurge: return ValueSurgeEnabled.Value ? ValueSurgeChance.Value : 0; case StageEffect.ValueCrash: return ValueCrashEnabled.Value ? ValueCrashChance.Value : 0; } } goto IL_076a; IL_076a: return 0; } private int RollConfiguredCount(ConfigEntry minimum, ConfigEntry maximum, string name) { var (num, num2) = OrderedRange(minimum.Value, maximum.Value, name); return Random.Range(num, num2 + 1); } private static void Shuffle(List effects) { for (int i = 0; i < effects.Count; i++) { int num = Random.Range(i, effects.Count); int index = i; int index2 = num; StageEffect value = effects[num]; StageEffect value2 = effects[i]; effects[index] = value; effects[index2] = value2; } } private static bool HasLogicalConflict(StageEffect candidate, List selected) { foreach (StageEffect item in selected) { if (IsPair(candidate, item, StageEffect.Freeze, StageEffect.Stun) || IsPair(candidate, item, StageEffect.EnemyRegen, StageEffect.EnemyPurge) || IsPair(candidate, item, StageEffect.EnemySpeedUp, StageEffect.EnemySpeedDown) || IsPair(candidate, item, StageEffect.Indestructible, StageEffect.Fragility) || IsPair(candidate, item, StageEffect.ValueSurge, StageEffect.ValueCrash)) { return true; } } return false; } private static bool IsPair(StageEffect first, StageEffect second, StageEffect left, StageEffect right) { if (first != left || second != right) { if (first == right) { return second == left; } return false; } return true; } private bool HasDangerousConflict(StageEffect candidate, List selected) { EffectRisk risk = GetRisk(candidate); int num = (((risk & EffectRisk.ValuableRisk) != 0) ? 1 : 0); foreach (StageEffect item in selected) { EffectRisk risk2 = GetRisk(item); if ((risk2 & EffectRisk.ValuableRisk) != EffectRisk.None) { num++; } if (RiskPairIsDangerous(risk, risk2)) { return true; } } return num >= 3; } private EffectRisk GetRisk(StageEffect effect) { EffectRisk effectRisk = effect switch { StageEffect.ZeroGravity => EffectRisk.ForcedMovement | EffectRisk.ValuableRisk, StageEffect.Roll => EffectRisk.ForcedMovement | EffectRisk.ValuableRisk, StageEffect.Void => EffectRisk.ForcedMovement | EffectRisk.LethalHazard | EffectRisk.AreaHazard | EffectRisk.ValuableRisk, StageEffect.Levitation => EffectRisk.ForcedMovement | EffectRisk.ValuableRisk, StageEffect.Shockwave => EffectRisk.ForcedMovement | EffectRisk.AreaHazard | EffectRisk.ValuableRisk, StageEffect.StunBlast => EffectRisk.AreaHazard, StageEffect.ExplosionRain => EffectRisk.LethalHazard | EffectRisk.AreaHazard | EffectRisk.ValuableRisk, StageEffect.EnemyWave => EffectRisk.EnemyPressure, StageEffect.EnemyWarp => EffectRisk.EnemyPressure, StageEffect.EnemyHunt => EffectRisk.EnemyPressure, StageEffect.EnemySpeedUp => EffectRisk.EnemyPressure, StageEffect.EnemyRegen => EffectRisk.EnemyPressure, StageEffect.DamagePulse => EffectRisk.LethalHazard, StageEffect.Knockback => EffectRisk.ForcedMovement, StageEffect.Quake => EffectRisk.ForcedMovement | EffectRisk.ValuableRisk, StageEffect.DoorChaos => EffectRisk.AreaHazard | EffectRisk.ValuableRisk, StageEffect.Fragility => EffectRisk.ValuableRisk | EffectRisk.ValueLossAmplifier, StageEffect.ValueCrash => EffectRisk.ValueLossAmplifier, StageEffect.GumballHypnosis => EffectRisk.ControlImpairment | EffectRisk.VisualImpairment, StageEffect.StarBarrage => EffectRisk.LethalHazard | EffectRisk.AreaHazard | EffectRisk.ValuableRisk, StageEffect.SpiderScare => EffectRisk.ControlImpairment | EffectRisk.VisualImpairment, StageEffect.Flicker => EffectRisk.VisualImpairment, StageEffect.TrafficShock => EffectRisk.ForcedMovement | EffectRisk.LethalHazard, StageEffect.DangerousValuables => EffectRisk.ForcedMovement | EffectRisk.LethalHazard | EffectRisk.AreaHazard | EffectRisk.ValuableRisk, _ => EffectRisk.None, }; if (effect == StageEffect.Minefield) { effectRisk = EffectRisk.AreaHazard; if (MinefieldExplosiveEnabled.Value) { effectRisk |= EffectRisk.LethalHazard | EffectRisk.ValuableRisk; } if (MinefieldShockwaveEnabled.Value) { effectRisk |= EffectRisk.ForcedMovement | EffectRisk.ValuableRisk; } } return effectRisk; } private static bool RiskPairIsDangerous(EffectRisk first, EffectRisk second) { bool flag = (first & EffectRisk.ForcedMovement) != 0; bool flag2 = (second & EffectRisk.ForcedMovement) != 0; bool flag3 = (first & EffectRisk.LethalHazard) != 0; bool flag4 = (second & EffectRisk.LethalHazard) != 0; bool flag5 = (first & EffectRisk.AreaHazard) != 0; bool flag6 = (second & EffectRisk.AreaHazard) != 0; bool flag7 = (first & EffectRisk.EnemyPressure) != 0; bool flag8 = (second & EffectRisk.EnemyPressure) != 0; bool flag9 = (first & EffectRisk.ControlImpairment) != 0; bool flag10 = (second & EffectRisk.ControlImpairment) != 0; bool flag11 = (first & EffectRisk.VisualImpairment) != 0; bool flag12 = (second & EffectRisk.VisualImpairment) != 0; bool flag13 = (first & EffectRisk.ValueLossAmplifier) != 0; bool flag14 = (second & EffectRisk.ValueLossAmplifier) != 0; bool flag15 = (first & EffectRisk.ValuableRisk) != 0; bool flag16 = (second & EffectRisk.ValuableRisk) != 0; if (!(flag && flag2) && !(flag3 && flag4) && (!flag || !(flag4 || flag6)) && (!flag2 || !(flag3 || flag5)) && !(flag7 && flag8) && !(flag3 && flag8) && !(flag4 && flag7) && !(flag9 && flag10) && (!flag9 || !(flag2 || flag4 || flag6 || flag8)) && (!flag10 || !(flag || flag3 || flag5 || flag7)) && !(flag11 && flag12) && !(flag13 && flag16)) { return flag14 && flag15; } return true; } private (int Min, int Max) OrderedRange(int first, int second, string name) { if (first <= second) { return (Min: first, Max: second); } string text = $"{name}:{first}:{second}"; if (_lastRangeWarning != text) { _lastRangeWarning = text; _logger.LogWarning((object)$"{name} minimum ({first}) exceeds maximum ({second}); using {second}..{first} without rewriting config."); } return (Min: second, Max: first); } private static ConfigEntry BindBool(ConfigFile file, string section, string key, bool value, string description) { return file.Bind(section, key, value, description); } private static ConfigEntry BindInt(ConfigFile file, string section, string key, int value, int min, int max, string description) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown return file.Bind(section, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(min, max), Array.Empty())); } private void MigrateSpawnCountDefaults(ConfigEntry minimum, ConfigEntry maximum, int previousMinimum, int previousMaximum, int newMinimum, int newMaximum, string eventName) { if (minimum.Value == previousMinimum && maximum.Value == previousMaximum) { minimum.Value = newMinimum; maximum.Value = newMaximum; _logger.LogInfo((object)("Migrated the previous default " + eventName + " spawn count from " + $"{previousMinimum}-{previousMaximum} to {newMinimum}-{newMaximum}.")); } } private int ReadConfigRevision(ConfigFile config, ConfigDefinition definition) { try { IDictionary orphanedEntries = GetOrphanedEntries(config); if (orphanedEntries != null && orphanedEntries.TryGetValue(definition, out var value) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return result; } } catch (Exception ex) { _logger.LogWarning((object)("Could not read the internal configuration revision: " + ex.Message)); } return 0; } private void WriteConfigRevision(ConfigFile config, ConfigDefinition definition) { try { IDictionary orphanedEntries = GetOrphanedEntries(config); if (orphanedEntries != null) { orphanedEntries[definition] = 9.ToString(CultureInfo.InvariantCulture); return; } } catch (Exception ex) { _logger.LogWarning((object)("Could not write the internal configuration revision: " + ex.Message)); return; } _logger.LogWarning((object)"Could not access the internal configuration revision store."); } private void RemoveObsoleteConfigEntry(ConfigFile config, string section, string key) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition(section, key); config.Remove(val); try { GetOrphanedEntries(config)?.Remove(val); } catch (Exception ex) { _logger.LogWarning((object)("Could not remove obsolete setting " + section + "." + key + ": " + ex.Message)); } } private static IDictionary? GetOrphanedEntries(ConfigFile config) { return OrphanedEntriesProperty?.GetValue(config) as IDictionary; } } internal enum EventRunState { Inactive, Waiting, Active, Countdown } internal enum EventMode { RandomEachEvent, FixedForStage, FixedPerExtraction, PersistentForStage } [Flags] internal enum StageEffect : long { None = 0L, Feather = 1L, ZeroGravity = 2L, Battery = 4L, Heal = 8L, Indestructible = 0x10L, Roll = 0x20L, Void = 0x40L, Levitation = 0x80L, Shockwave = 0x100L, StunBlast = 0x200L, ExplosionRain = 0x400L, EnemyWave = 0x800L, Minefield = 0x1000L, Freeze = 0x2000L, Stun = 0x4000L, EnemyWarp = 0x8000L, EnemyHunt = 0x10000L, EnemyRegen = 0x20000L, EnemyPurge = 0x40000L, DamagePulse = 0x80000L, SecondChance = 0x100000L, Knockback = 0x200000L, Flicker = 0x400000L, Quake = 0x800000L, DoorChaos = 0x1000000L, HealingAura = 0x2000000L, StarBarrage = 0x4000000L, ValueSurge = 0x8000000L, ValueCrash = 0x10000000L, GumballHypnosis = 0x20000000L, Fragility = 0x40000000L, SpiderScare = 0x80000000L, TrafficShock = 0x100000000L, DangerousValuables = 0x200000000L, EnemySpeedUp = 0x400000000L, EnemySpeedDown = 0x800000000L } internal static class StageEffectSet { internal const StageEffect All = StageEffect.Feather | StageEffect.ZeroGravity | StageEffect.Battery | StageEffect.Heal | StageEffect.Indestructible | StageEffect.Roll | StageEffect.Void | StageEffect.Levitation | StageEffect.Shockwave | StageEffect.StunBlast | StageEffect.ExplosionRain | StageEffect.EnemyWave | StageEffect.Minefield | StageEffect.Freeze | StageEffect.Stun | StageEffect.EnemyWarp | StageEffect.EnemyHunt | StageEffect.EnemyRegen | StageEffect.EnemyPurge | StageEffect.DamagePulse | StageEffect.SecondChance | StageEffect.Knockback | StageEffect.Flicker | StageEffect.Quake | StageEffect.DoorChaos | StageEffect.HealingAura | StageEffect.StarBarrage | StageEffect.ValueSurge | StageEffect.ValueCrash | StageEffect.GumballHypnosis | StageEffect.Fragility | StageEffect.SpiderScare | StageEffect.TrafficShock | StageEffect.DangerousValuables | StageEffect.EnemySpeedUp | StageEffect.EnemySpeedDown; internal static readonly IReadOnlyList IndividualEffects = new StageEffect[36] { StageEffect.Feather, StageEffect.ZeroGravity, StageEffect.Battery, StageEffect.Heal, StageEffect.Indestructible, StageEffect.Fragility, StageEffect.Roll, StageEffect.Void, StageEffect.Levitation, StageEffect.Shockwave, StageEffect.StunBlast, StageEffect.ExplosionRain, StageEffect.EnemyWave, StageEffect.Minefield, StageEffect.Freeze, StageEffect.Stun, StageEffect.EnemyWarp, StageEffect.EnemyHunt, StageEffect.EnemyRegen, StageEffect.EnemyPurge, StageEffect.DamagePulse, StageEffect.SecondChance, StageEffect.Knockback, StageEffect.Flicker, StageEffect.Quake, StageEffect.DoorChaos, StageEffect.ValueSurge, StageEffect.ValueCrash, StageEffect.GumballHypnosis, StageEffect.HealingAura, StageEffect.StarBarrage, StageEffect.SpiderScare, StageEffect.TrafficShock, StageEffect.DangerousValuables, StageEffect.EnemySpeedUp, StageEffect.EnemySpeedDown }; internal static bool Contains(StageEffect effects, StageEffect effect) { if (effect != StageEffect.None) { return (effects & effect) == effect; } return false; } internal static bool IsValid(StageEffect effects) { return (effects & ~(StageEffect.Feather | StageEffect.ZeroGravity | StageEffect.Battery | StageEffect.Heal | StageEffect.Indestructible | StageEffect.Roll | StageEffect.Void | StageEffect.Levitation | StageEffect.Shockwave | StageEffect.StunBlast | StageEffect.ExplosionRain | StageEffect.EnemyWave | StageEffect.Minefield | StageEffect.Freeze | StageEffect.Stun | StageEffect.EnemyWarp | StageEffect.EnemyHunt | StageEffect.EnemyRegen | StageEffect.EnemyPurge | StageEffect.DamagePulse | StageEffect.SecondChance | StageEffect.Knockback | StageEffect.Flicker | StageEffect.Quake | StageEffect.DoorChaos | StageEffect.HealingAura | StageEffect.StarBarrage | StageEffect.ValueSurge | StageEffect.ValueCrash | StageEffect.GumballHypnosis | StageEffect.Fragility | StageEffect.SpiderScare | StageEffect.TrafficShock | StageEffect.DangerousValuables | StageEffect.EnemySpeedUp | StageEffect.EnemySpeedDown)) == 0; } internal static string Format(StageEffect effects, string separator) { StringBuilder stringBuilder = new StringBuilder(); foreach (StageEffect individualEffect in IndividualEffects) { if (Contains(effects, individualEffect)) { if (stringBuilder.Length > 0) { stringBuilder.Append(separator); } StringBuilder stringBuilder2 = stringBuilder; stringBuilder2.Append(individualEffect switch { StageEffect.Battery => "Battery Charge", StageEffect.ZeroGravity => "Zero Gravity", StageEffect.StunBlast => "Stun Blast", StageEffect.ExplosionRain => "Explosion Rain", StageEffect.EnemyWave => "Enemy Wave", StageEffect.EnemyWarp => "Enemy Warp", StageEffect.EnemyHunt => "Enemy Hunt", StageEffect.EnemyRegen => "Enemy Regen", StageEffect.EnemyPurge => "Enemy Purge", StageEffect.DamagePulse => "Damage Pulse", StageEffect.SecondChance => "Second Chance", StageEffect.DoorChaos => "Door Chaos", StageEffect.ValueSurge => "Value Surge", StageEffect.ValueCrash => "Value Crash", StageEffect.GumballHypnosis => "Gumball Hypnosis", StageEffect.HealingAura => "Healing Aura", StageEffect.StarBarrage => "Star Barrage", StageEffect.SpiderScare => "Spider Scare", StageEffect.TrafficShock => "Traffic Shock", StageEffect.DangerousValuables => "Dangerous Valuables", StageEffect.EnemySpeedUp => "Enemy Speed Up", StageEffect.EnemySpeedDown => "Enemy Speed Down", StageEffect.Roll => "Roll", _ => individualEffect.ToString(), }); } } if (stringBuilder.Length <= 0) { return "None"; } return stringBuilder.ToString(); } internal static string FormatForChat(StageEffect effects, string separator) { return Format(effects, separator).Replace(" ", string.Empty); } } internal readonly struct HudState { internal EventRunState State { get; } internal StageEffect Effect { get; } internal StageEffect PreviewEffect { get; } internal EventMode Mode { get; } internal int DurationSeconds { get; } internal int IntervalSeconds { get; } internal int RemainingSeconds { get; } internal int PhaseDurationSeconds { get; } internal int SlotLimit { get; } internal HudState(EventRunState state, StageEffect effect, StageEffect previewEffect, EventMode mode, int durationSeconds, int intervalSeconds, int remainingSeconds, int phaseDurationSeconds, int slotLimit) { State = state; Effect = effect; PreviewEffect = previewEffect; Mode = mode; DurationSeconds = durationSeconds; IntervalSeconds = intervalSeconds; RemainingSeconds = remainingSeconds; PhaseDurationSeconds = phaseDurationSeconds; SlotLimit = slotLimit; } } internal readonly struct TargetFilter { internal bool Valuables { get; } internal bool CosmeticBoxes { get; } internal bool Items { get; } internal bool Doors { get; } internal bool Weapons { get; } internal bool Players { get; } internal bool Enemies { get; } internal TargetFilter(bool valuables, bool cosmeticBoxes, bool items, bool doors, bool weapons, bool players, bool enemies) { Valuables = valuables; CosmeticBoxes = cosmeticBoxes; Items = items; Doors = doors; Weapons = weapons; Players = players; Enemies = enemies; } internal TargetFilter WithDoors(bool doors) { return new TargetFilter(Valuables, CosmeticBoxes, Items, doors, Weapons, Players, Enemies); } internal bool Allows(PhysGrabObject physObject) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)((Component)physObject).GetComponentInParent() != (Object)null) { return Players; } if ((Object)(object)((Component)physObject).GetComponent() != (Object)null || (Object)(object)((Component)physObject).GetComponentInParent() != (Object)null || (Object)(object)((Component)physObject).GetComponentInParent() != (Object)null) { return Enemies; } if ((Object)(object)((Component)physObject).GetComponent() != (Object)null) { return CosmeticBoxes; } if ((Object)(object)((Component)physObject).GetComponent() != (Object)null || (Object)(object)((Component)physObject).GetComponentInParent() != (Object)null || (Object)(object)((Component)physObject).GetComponentInChildren(true) != (Object)null) { return Doors; } if ((Object)(object)((Component)physObject).GetComponent() != (Object)null) { return Valuables; } ItemAttributes component = ((Component)physObject).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.item != (Object)null && IsWeapon(component.item.itemType)) { return Weapons; } return Items; } private static bool IsWeapon(itemType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 if ((int)type != 9 && (int)type != 7 && (int)type != 15 && (int)type != 6) { return (int)type == 11; } return true; } } internal sealed class StagePhysicsEffectCarrierMarker : MonoBehaviour { } internal static class PlayerAvatarState { private static readonly FieldInfo? DisabledField = AccessTools.Field(typeof(PlayerAvatar), "isDisabled"); private static readonly FieldInfo? DeadField = AccessTools.Field(typeof(PlayerAvatar), "deadSet"); private static readonly FieldInfo? SpectatingField = AccessTools.Field(typeof(PlayerAvatar), "spectating"); private static readonly FieldInfo? HealthField = AccessTools.Field(typeof(PlayerHealth), "health"); internal static bool IsLiving(PlayerAvatar? player) { if ((Object)(object)player == (Object)null || !((Component)player).gameObject.activeInHierarchy || ReadBool(DisabledField, player) || ReadBool(DeadField, player) || ReadBool(SpectatingField, player)) { return false; } if (!((Object)(object)player.playerHealth == (Object)null) && HealthField?.GetValue(player.playerHealth) is int num) { return num > 0; } return true; } private static bool ReadBool(FieldInfo? field, object instance) { object obj = field?.GetValue(instance); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } } internal static class PlayerEffectCarrierUtility { private const float VerticalOffset = 0.75f; internal static Vector3 Position(PlayerAvatar player) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) return ((Component)player).transform.position + Vector3.up * 0.75f; } internal static void DisablePhysicalInteraction(GameObject carrier) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) Collider[] componentsInChildren = carrier.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = false; } Rigidbody[] componentsInChildren2 = carrier.GetComponentsInChildren(true); foreach (Rigidbody val in componentsInChildren2) { if (!val.isKinematic) { val.velocity = Vector3.zero; val.angularVelocity = Vector3.zero; } val.isKinematic = true; val.detectCollisions = false; } } } internal static class PhysGrabObjectRegistry { private static readonly HashSet Objects = new HashSet(); private static readonly List Stale = new List(); internal static void Register(PhysGrabObject? physObject) { if ((Object)(object)physObject != (Object)null) { Objects.Add(physObject); } } internal static void SeedFromScene() { PhysGrabObject[] array = Object.FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { Register(array[i]); } } internal static List Snapshot(TargetFilter filter, bool discoverEnemies = true) { List list = new List(Objects.Count); HashSet hashSet = new HashSet(); Stale.Clear(); foreach (PhysGrabObject @object in Objects) { if ((Object)(object)@object == (Object)null) { continue; } try { if ((Object)(object)((Component)@object).GetComponent() == (Object)null && ((Component)@object).gameObject.activeInHierarchy && (Object)(object)@object.rb != (Object)null && filter.Allows(@object) && hashSet.Add(@object)) { list.Add(@object); } } catch { Stale.Add(@object); } } foreach (PhysGrabObject item in Stale) { Objects.Remove(item); } Objects.RemoveWhere((PhysGrabObject physObject) => (Object)(object)physObject == (Object)null); if (filter.Enemies && discoverEnemies) { EnemyRigidbody[] array = Object.FindObjectsOfType(); foreach (EnemyRigidbody val in array) { if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy) { PhysGrabObject val2 = ((Component)val).GetComponent() ?? ((Component)val).GetComponentInChildren(true) ?? ((Component)val).GetComponentInParent(); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.rb == (Object)null) && !((Object)(object)((Component)val2).GetComponent() != (Object)null) && hashSet.Add(val2)) { Objects.Add(val2); list.Add(val2); } } } } return list; } internal static void Clear() { Objects.Clear(); Stale.Clear(); } } internal static class DeferredObjectCleanupQueue { private readonly struct PendingObject { internal GameObject Instance { get; } internal int InstanceId { get; } internal PendingObject(GameObject instance, int instanceId) { Instance = instance; InstanceId = instanceId; } } private const int MaximumDestroysPerFrame = 2; private static readonly Queue Pending = new Queue(); private static readonly Queue PendingActions = new Queue(); private static readonly HashSet PendingIds = new HashSet(); internal static void Enqueue(GameObject? instance) { if (!((Object)(object)instance == (Object)null)) { int instanceID = ((Object)instance).GetInstanceID(); if (PendingIds.Add(instanceID)) { Pending.Enqueue(new PendingObject(instance, instanceID)); } } } internal static void Enqueue(IEnumerable instances) { foreach (GameObject instance in instances) { Enqueue(instance); } } internal static void Enqueue(Action action) { if (action != null) { PendingActions.Enqueue(action); } } internal static void ProcessFrame() { int num = 0; while (num < 2 && (Pending.Count > 0 || PendingActions.Count > 0)) { if (PendingActions.Count > 0) { try { PendingActions.Dequeue()(); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Deferred event cleanup action failed: " + ex.GetBaseException().Message)); } num++; continue; } PendingObject pendingObject = Pending.Dequeue(); PendingIds.Remove(pendingObject.InstanceId); GameObject instance = pendingObject.Instance; if (!((Object)(object)instance == (Object)null)) { DestroyNow(instance); num++; } } } internal static void ClearReferences() { Pending.Clear(); PendingActions.Clear(); PendingIds.Clear(); } private static void DestroyNow(GameObject instance) { try { PhotonView val = instance.GetComponent() ?? instance.GetComponentInParent() ?? instance.GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)val != (Object)null && val.ViewID != 0) { PhotonNetwork.Destroy(((Component)val).gameObject); } else { Object.Destroy((Object)(object)instance); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Deferred event-object cleanup left to scene unload: " + ex.Message)); } } } internal sealed class VanillaEffectResolver { private const int MaxAttempts = 5; private int _featherAttempts; private int _zeroGravityAttempts; private int _rollStaffAttempts; private int _voidAttempts; private bool _featherFailed; private bool _zeroGravityFailed; private bool _rollStaffFailed; private bool _voidFailed; private readonly Dictionary _orbPrefabs = new Dictionary(); private readonly Dictionary _orbResourcePaths = new Dictionary(); private readonly Dictionary _orbAttempts = new Dictionary(); private readonly HashSet _orbFailures = new HashSet(); internal GameObject? FeatherPrefab { get; private set; } internal string? FeatherResourcePath { get; private set; } internal GameObject? ZeroGravityProjectilePrefab { get; private set; } internal GameObject? ZeroGravityAffectPrefab { get; private set; } internal string? ZeroGravityProjectileResourcePath { get; private set; } internal GameObject? RollStaffProjectilePrefab { get; private set; } internal GameObject? RollStaffAffectPrefab { get; private set; } internal string? RollStaffProjectileResourcePath { get; private set; } internal GameObject? VoidPrefab { get; private set; } internal string? VoidResourcePath { get; private set; } internal bool TryResolveOrb(StageEffect effect, out GameObject? prefab, out string? resourcePath) { if (_orbPrefabs.TryGetValue(effect, out GameObject value) && _orbResourcePaths.TryGetValue(effect, out string value2)) { prefab = value; resourcePath = value2; return true; } prefab = null; resourcePath = null; if (_orbFailures.Contains(effect)) { return false; } Type type = OrbComponentType(effect); string text = OrbItemName(effect); if (type == null || string.IsNullOrEmpty(text)) { return false; } Item val = Resources.Load("Items/" + text) ?? Resources.Load("Items/Removed Items/" + text); if (!IsOrbItem(val, type)) { Item[] array = Resources.FindObjectsOfTypeAll(); foreach (Item val2 in array) { if (IsOrbItem(val2, type)) { val = val2; break; } } } if (IsOrbItem(val, type)) { prefab = ((PrefabRef)(object)val.prefab).Prefab; resourcePath = ((PrefabRef)(object)val.prefab).ResourcePath; _orbPrefabs[effect] = prefab; _orbResourcePaths[effect] = resourcePath; StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Resolved vanilla {effect} Orb: {resourcePath}"); return true; } int value3; int num = ((!_orbAttempts.TryGetValue(effect, out value3)) ? 1 : (value3 + 1)); _orbAttempts[effect] = num; if (num >= 5) { _orbFailures.Add(effect); StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Vanilla {effect} Orb could not be resolved; the event is unavailable."); } return false; } internal bool ResolveFeather() { if ((Object)(object)FeatherPrefab != (Object)null && !string.IsNullOrEmpty(FeatherResourcePath)) { return true; } if (_featherFailed) { return false; } Item val = Resources.Load("Items/Item Orb Feather") ?? Resources.Load("Items/Removed Items/Item Orb Feather"); if (!IsFeatherItem(val)) { Item[] array = Resources.FindObjectsOfTypeAll(); foreach (Item val2 in array) { if (IsFeatherItem(val2)) { val = val2; break; } } } if (IsFeatherItem(val)) { FeatherPrefab = ((PrefabRef)(object)val.prefab).Prefab; FeatherResourcePath = ((PrefabRef)(object)val.prefab).ResourcePath; StagePhysicsEventsPlugin.ModLogger.LogInfo((object)("Resolved vanilla Feather Orb: " + FeatherResourcePath)); return true; } if (++_featherAttempts >= 5) { _featherFailed = true; StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Vanilla Feather Orb could not be resolved; Feather events are unavailable."); } return false; } internal bool ResolveZeroGravity() { if ((Object)(object)ZeroGravityProjectilePrefab != (Object)null && (Object)(object)ZeroGravityAffectPrefab != (Object)null && !string.IsNullOrEmpty(ZeroGravityProjectileResourcePath)) { return true; } if (_zeroGravityFailed || (Object)(object)StatsManager.instance == (Object)null) { return false; } int num = 0; foreach (Item value in StatsManager.instance.itemDictionary.Values) { GameObject val = ((!((Object)(object)value != (Object)null)) ? null : ((PrefabRef)(object)value.prefab)?.Prefab); ItemStaffZeroGravity val2 = (((Object)(object)val != (Object)null) ? val.GetComponentInChildren(true) : null); if ((Object)(object)val2 == (Object)null) { continue; } PrefabRef projectilePrefab = val2.projectilePrefab; if (projectilePrefab == null || (Object)(object)((PrefabRef)(object)projectilePrefab).Bundle != (Object)null || !((PrefabRef)(object)projectilePrefab).IsValid() || string.IsNullOrEmpty(((PrefabRef)(object)projectilePrefab).ResourcePath)) { num++; continue; } GameObject prefab = ((PrefabRef)(object)projectilePrefab).Prefab; GameObject val3 = ((PrefabRef)(object)(((Object)(object)prefab != (Object)null) ? prefab.GetComponentInChildren(true) : null)?.semiAffectPrefab)?.Prefab; if ((Object)(object)val3 == (Object)null || (Object)(object)val3.GetComponentInChildren(true) == (Object)null) { continue; } ZeroGravityProjectilePrefab = prefab; ZeroGravityAffectPrefab = val3; ZeroGravityProjectileResourcePath = ((PrefabRef)(object)projectilePrefab).ResourcePath; StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Resolved vanilla zero-gravity projectile: {ZeroGravityProjectileResourcePath} (skipped look-alikes: {num})."); return true; } if (++_zeroGravityAttempts >= 5) { _zeroGravityFailed = true; StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Vanilla zero-gravity staff could not be resolved; Zero Gravity events are unavailable."); } return false; } internal bool ResolveRollStaff() { if ((Object)(object)RollStaffProjectilePrefab != (Object)null && (Object)(object)RollStaffAffectPrefab != (Object)null && !string.IsNullOrEmpty(RollStaffProjectileResourcePath)) { return true; } if (_rollStaffFailed || (Object)(object)StatsManager.instance == (Object)null) { return false; } int num = 0; foreach (Item value in StatsManager.instance.itemDictionary.Values) { GameObject val = ((!((Object)(object)value != (Object)null)) ? null : ((PrefabRef)(object)value.prefab)?.Prefab); ItemStaffTorque val2 = (((Object)(object)val != (Object)null) ? val.GetComponentInChildren(true) : null); if ((Object)(object)val2 == (Object)null) { continue; } PrefabRef projectilePrefab = val2.projectilePrefab; if (projectilePrefab == null || (Object)(object)((PrefabRef)(object)projectilePrefab).Bundle != (Object)null || !((PrefabRef)(object)projectilePrefab).IsValid() || string.IsNullOrEmpty(((PrefabRef)(object)projectilePrefab).ResourcePath)) { num++; continue; } GameObject prefab = ((PrefabRef)(object)projectilePrefab).Prefab; GameObject val3 = ((PrefabRef)(object)(((Object)(object)prefab != (Object)null) ? prefab.GetComponentInChildren(true) : null)?.semiAffectPrefab)?.Prefab; if ((Object)(object)val3 == (Object)null || (Object)(object)val3.GetComponentInChildren(true) == (Object)null) { continue; } RollStaffProjectilePrefab = prefab; RollStaffAffectPrefab = val3; RollStaffProjectileResourcePath = ((PrefabRef)(object)projectilePrefab).ResourcePath; StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Resolved vanilla Roll Staff projectile: {RollStaffProjectileResourcePath} (skipped look-alikes: {num})."); return true; } if (++_rollStaffAttempts >= 5) { _rollStaffFailed = true; StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Vanilla Roll Staff could not be resolved; Roll Staff events are unavailable."); } return false; } internal bool ResolveVoid() { if ((Object)(object)VoidPrefab != (Object)null && !string.IsNullOrEmpty(VoidResourcePath)) { return true; } if (_voidFailed) { return false; } ItemStaffVoid val = ResolveVoidStaff(Resources.Load("Items/Item Staff Void") ?? Resources.Load("Items/Removed Items/Item Staff Void")); if ((Object)(object)val == (Object)null) { Item[] array = Resources.FindObjectsOfTypeAll(); for (int i = 0; i < array.Length; i++) { val = ResolveVoidStaff(array[i]); if ((Object)(object)val != (Object)null) { break; } } } PrefabRef val2 = val?.prefabToInstantiateOnHit; if (val2 != null && (Object)(object)((PrefabRef)(object)val2).Bundle == (Object)null && ((PrefabRef)(object)val2).IsValid() && !string.IsNullOrEmpty(((PrefabRef)(object)val2).ResourcePath)) { VoidPrefab = ((PrefabRef)(object)val2).Prefab; VoidResourcePath = ((PrefabRef)(object)val2).ResourcePath; StagePhysicsEventsPlugin.ModLogger.LogInfo((object)("Resolved vanilla Void Staff effect: " + VoidResourcePath)); return true; } if (++_voidAttempts >= 5) { _voidFailed = true; StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Vanilla Void Staff effect could not be resolved; the Void event is unavailable."); } return false; } private static bool IsFeatherItem(Item? item) { try { if ((Object)(object)item == (Object)null || item.prefab == null || (Object)(object)((PrefabRef)(object)item.prefab).Bundle != (Object)null || !((PrefabRef)(object)item.prefab).IsValid() || string.IsNullOrEmpty(((PrefabRef)(object)item.prefab).ResourcePath)) { return false; } GameObject prefab = ((PrefabRef)(object)item.prefab).Prefab; return (Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponentInChildren(true) != (Object)null; } catch (Exception) { return false; } } private static bool IsOrbItem(Item? item, Type componentType) { try { if ((Object)(object)item == (Object)null || item.prefab == null || (Object)(object)((PrefabRef)(object)item.prefab).Bundle != (Object)null || !((PrefabRef)(object)item.prefab).IsValid() || string.IsNullOrEmpty(((PrefabRef)(object)item.prefab).ResourcePath)) { return false; } GameObject prefab = ((PrefabRef)(object)item.prefab).Prefab; return (Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponentInChildren(componentType, true) != (Object)null; } catch (Exception) { return false; } } private static ItemStaffVoid? ResolveVoidStaff(Item? item) { try { if ((Object)(object)item == (Object)null || item.prefab == null || (Object)(object)((PrefabRef)(object)item.prefab).Bundle != (Object)null || !((PrefabRef)(object)item.prefab).IsValid() || string.IsNullOrEmpty(((PrefabRef)(object)item.prefab).ResourcePath)) { return null; } GameObject prefab = ((PrefabRef)(object)item.prefab).Prefab; return (prefab != null) ? prefab.GetComponentInChildren(true) : null; } catch (Exception) { return null; } } private static Type? OrbComponentType(StageEffect effect) { return effect switch { StageEffect.Battery => typeof(ItemOrbBattery), StageEffect.Heal => typeof(ItemOrbHeal), StageEffect.Indestructible => typeof(ItemOrbIndestructible), _ => null, }; } private static string? OrbItemName(StageEffect effect) { return effect switch { StageEffect.Battery => "Item Orb Battery", StageEffect.Heal => "Item Orb Heal", StageEffect.Indestructible => "Item Orb Indestructible", _ => null, }; } } internal readonly struct ResolvedSpawnPrefab { internal GameObject Prefab { get; } internal string ResourcePath { get; } internal ResolvedSpawnPrefab(GameObject prefab, string resourcePath) { Prefab = prefab; ResourcePath = resourcePath; } } internal readonly struct ResolvedMinePrefab { internal ResolvedSpawnPrefab SpawnPrefab { get; } internal MineType MineType { get; } internal ResolvedMinePrefab(ResolvedSpawnPrefab spawnPrefab, MineType mineType) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) SpawnPrefab = spawnPrefab; MineType = mineType; } } internal enum DangerousValuableKind { IceSaw, Blender, Flamethrower, Egg, Car, Plane, Broom } internal enum LevitationActivationKind { None, ImpactLight, ImpactMedium, ImpactHeavy, BreakLight, BreakMedium, BreakHeavy, Destroy } internal sealed class VanillaSpawnEffectResolver { private const int MaxAttempts = 5; private readonly Dictionary _prefabs = new Dictionary(); private readonly Dictionary _attempts = new Dictionary(); private readonly HashSet _failures = new HashSet(); private readonly List _minePrefabs = new List(); private int _mineAttempts; private bool _mineFailed; internal LevitationActivationKind LevitationActivation { get; private set; } internal bool TryResolve(StageEffect effect, out ResolvedSpawnPrefab resolved) { if (_prefabs.TryGetValue(effect, out resolved)) { return true; } if (_failures.Contains(effect)) { return false; } if (effect switch { StageEffect.Levitation => TryResolveLevitation(out resolved), StageEffect.GumballHypnosis => TryResolveGumball(out resolved), StageEffect.HealingAura => TryResolveHealingAura(out resolved), StageEffect.StarBarrage => TryResolveStarProjectile(out resolved), StageEffect.SpiderScare => TryResolveValuableComponent(out resolved), StageEffect.TrafficShock => TryResolveValuableComponent(out resolved), StageEffect.Flicker => TryResolveValuableComponent(out resolved), StageEffect.EnemyHunt => TryResolveValuableComponent(out resolved), _ => TryResolveItem(effect, out resolved), }) { _prefabs[effect] = resolved; StagePhysicsEventsPlugin.ModLogger.LogInfo((object)("Resolved vanilla " + StageEffectSet.Format(effect, string.Empty) + " prefab: " + resolved.ResourcePath)); return true; } int value; int num = ((!_attempts.TryGetValue(effect, out value)) ? 1 : (value + 1)); _attempts[effect] = num; if (num >= 5) { _failures.Add(effect); StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Vanilla " + StageEffectSet.Format(effect, string.Empty) + " prefab could not be resolved; the event is unavailable.")); } resolved = default(ResolvedSpawnPrefab); return false; } internal bool TryResolveMines(StagePhysicsConfig config, out IReadOnlyList prefabs) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) if (_minePrefabs.Count > 0) { prefabs = EnabledMinePrefabs(config); return prefabs.Count > 0; } if (_mineFailed) { prefabs = Array.Empty(); return false; } HashSet hashSet = new HashSet(StringComparer.Ordinal); foreach (Item item in EnumerateItems()) { if (TryGetVanillaPrefab(item, out var resolved)) { ItemMine componentInChildren = resolved.Prefab.GetComponentInChildren(true); if (!((Object)(object)componentInChildren == (Object)null) && (int)componentInChildren.mineType != 0 && hashSet.Add(resolved.ResourcePath)) { _minePrefabs.Add(new ResolvedMinePrefab(resolved, componentInChildren.mineType)); } } } if (_minePrefabs.Count == 0 && ++_mineAttempts >= 5) { _mineFailed = true; StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Vanilla mine prefabs could not be resolved; Minefield is unavailable."); } else if (_minePrefabs.Count > 0) { StagePhysicsEventsPlugin.ModLogger.LogInfo((object)string.Format("Resolved {0} vanilla mine prefab(s): {1}", _minePrefabs.Count, string.Join(", ", _minePrefabs.ConvertAll((ResolvedMinePrefab entry) => ((object)entry.MineType/*cast due to .constrained prefix*/).ToString())))); } prefabs = EnabledMinePrefabs(config); return prefabs.Count > 0; } private IReadOnlyList EnabledMinePrefabs(StagePhysicsConfig config) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected I4, but got Unknown List list = new List(); foreach (ResolvedMinePrefab minePrefab in _minePrefabs) { MineType mineType = minePrefab.MineType; if ((mineType - 1) switch { 0 => config.MinefieldExplosiveEnabled.Value, 1 => config.MinefieldShockwaveEnabled.Value, 2 => config.MinefieldStunEnabled.Value, _ => false, }) { list.Add(minePrefab); } } return list; } private bool TryResolveItem(StageEffect effect, out ResolvedSpawnPrefab resolved) { Type type = effect switch { StageEffect.Shockwave => typeof(ItemGrenadeShockwave), StageEffect.StunBlast => typeof(ItemGrenadeStun), StageEffect.ExplosionRain => typeof(ItemGrenadeExplosive), _ => null, }; if (type == null) { resolved = default(ResolvedSpawnPrefab); return false; } foreach (Item item in EnumerateItems()) { if (TryGetVanillaPrefab(item, out var resolved2) && !((Object)(object)resolved2.Prefab.GetComponentInChildren(type, true) == (Object)null) && !((Object)(object)resolved2.Prefab.GetComponentInChildren(true) == (Object)null) && !((Object)(object)resolved2.Prefab.GetComponentInChildren(true) == (Object)null)) { resolved = resolved2; return true; } } resolved = default(ResolvedSpawnPrefab); return false; } private bool TryResolveLevitation(out ResolvedSpawnPrefab resolved) { LevelValuables[] array = Resources.FindObjectsOfTypeAll(); for (int i = 0; i < array.Length; i++) { foreach (PrefabRef item in EnumerateValuableRefs(array[i])) { if (!TryGetVanillaPrefab(item, out var resolved2)) { continue; } ValuableLevitationPotion componentInChildren = resolved2.Prefab.GetComponentInChildren(true); PhysGrabObjectImpactDetector componentInChildren2 = resolved2.Prefab.GetComponentInChildren(true); if (!((Object)(object)componentInChildren == (Object)null) && !((Object)(object)componentInChildren2 == (Object)null)) { LevitationActivationKind levitationActivationKind = FindLevitationActivation(componentInChildren2); if (levitationActivationKind != LevitationActivationKind.None) { LevitationActivation = levitationActivationKind; resolved = resolved2; return true; } } } } resolved = default(ResolvedSpawnPrefab); return false; } private static bool TryResolveGumball(out ResolvedSpawnPrefab resolved) { LevelValuables[] array = Resources.FindObjectsOfTypeAll(); for (int i = 0; i < array.Length; i++) { foreach (PrefabRef item in EnumerateValuableRefs(array[i])) { if (TryGetVanillaPrefab(item, out var resolved2) && !((Object)(object)resolved2.Prefab.GetComponentInChildren(true) == (Object)null) && !((Object)(object)resolved2.Prefab.GetComponentInChildren(true) == (Object)null)) { resolved = resolved2; return true; } } } resolved = default(ResolvedSpawnPrefab); return false; } private static bool TryResolveHealingAura(out ResolvedSpawnPrefab resolved) { LevelValuables[] array = Resources.FindObjectsOfTypeAll(); for (int i = 0; i < array.Length; i++) { foreach (PrefabRef item in EnumerateValuableRefs(array[i])) { if (TryGetVanillaPrefab(item, out var resolved2)) { ValuableSmallPotion componentInChildren = resolved2.Prefab.GetComponentInChildren(true); if (!((Object)(object)componentInChildren?.healAura == (Object)null) && !((Object)(object)componentInChildren.healAura.GetComponent() == (Object)null)) { resolved = new ResolvedSpawnPrefab(componentInChildren.healAura, "Enemies/" + ((Object)componentInChildren.healAura).name); return true; } } } } resolved = default(ResolvedSpawnPrefab); return false; } private static bool TryResolveStarProjectile(out ResolvedSpawnPrefab resolved) { LevelValuables[] array = Resources.FindObjectsOfTypeAll(); for (int i = 0; i < array.Length; i++) { foreach (PrefabRef item in EnumerateValuableRefs(array[i])) { if (TryGetVanillaPrefab(item, out var resolved2)) { ValuableStarWand componentInChildren = resolved2.Prefab.GetComponentInChildren(true); if (!((Object)(object)componentInChildren == (Object)null) && TryGetVanillaPrefab(componentInChildren.bulletPrefab, out var resolved3) && !((Object)(object)resolved3.Prefab.GetComponentInChildren(true) == (Object)null)) { resolved = resolved3; return true; } } } } resolved = default(ResolvedSpawnPrefab); return false; } private static bool TryResolveValuableComponent(out ResolvedSpawnPrefab resolved) where T : Component { LevelValuables[] array = Resources.FindObjectsOfTypeAll(); for (int i = 0; i < array.Length; i++) { foreach (PrefabRef item in EnumerateValuableRefs(array[i])) { if (TryGetVanillaPrefab(item, out var resolved2) && (Object)(object)resolved2.Prefab.GetComponentInChildren(true) != (Object)null) { resolved = resolved2; return true; } } } resolved = default(ResolvedSpawnPrefab); return false; } private static LevitationActivationKind FindLevitationActivation(PhysGrabObjectImpactDetector detector) { return FindActivation(detector, "ActivateSphere"); } private static LevitationActivationKind FindActivation(PhysGrabObjectImpactDetector detector, string methodName) { (UnityEvent, LevitationActivationKind)[] array = new(UnityEvent, LevitationActivationKind)[7] { (detector.onImpactLight, LevitationActivationKind.ImpactLight), (detector.onImpactMedium, LevitationActivationKind.ImpactMedium), (detector.onImpactHeavy, LevitationActivationKind.ImpactHeavy), (detector.onBreakLight, LevitationActivationKind.BreakLight), (detector.onBreakMedium, LevitationActivationKind.BreakMedium), (detector.onBreakHeavy, LevitationActivationKind.BreakHeavy), (detector.onDestroy, LevitationActivationKind.Destroy) }; for (int i = 0; i < array.Length; i++) { var (val, result) = array[i]; if (val == null) { continue; } for (int j = 0; j < ((UnityEventBase)val).GetPersistentEventCount(); j++) { if (string.Equals(((UnityEventBase)val).GetPersistentMethodName(j), methodName, StringComparison.Ordinal)) { return result; } } } return LevitationActivationKind.None; } private static IEnumerable EnumerateItems() { HashSet seen = new HashSet(); if ((Object)(object)StatsManager.instance != (Object)null) { foreach (Item value in StatsManager.instance.itemDictionary.Values) { if ((Object)(object)value != (Object)null && seen.Add(value)) { yield return value; } } } Item[] array = Resources.FindObjectsOfTypeAll(); foreach (Item val in array) { if ((Object)(object)val != (Object)null && seen.Add(val)) { yield return val; } } } private static IEnumerable EnumerateValuableRefs(LevelValuables preset) { List[] array = new List[7] { preset.tiny, preset.small, preset.medium, preset.big, preset.wide, preset.tall, preset.veryTall }; List[] array2 = array; foreach (List list in array2) { if (list == null) { continue; } foreach (PrefabRef item in list) { if (item != null) { yield return item; } } } } private static bool TryGetVanillaPrefab(Item item, out ResolvedSpawnPrefab resolved) { if ((Object)(object)item == (Object)null) { resolved = default(ResolvedSpawnPrefab); return false; } return TryGetVanillaPrefab(item.prefab, out resolved); } private static bool TryGetVanillaPrefab(PrefabRef prefabRef, out ResolvedSpawnPrefab resolved) { try { if (prefabRef == null || (Object)(object)((PrefabRef)(object)prefabRef).Bundle != (Object)null || !((PrefabRef)(object)prefabRef).IsValid() || string.IsNullOrEmpty(((PrefabRef)(object)prefabRef).ResourcePath) || (Object)(object)((PrefabRef)(object)prefabRef).Prefab == (Object)null) { resolved = default(ResolvedSpawnPrefab); return false; } resolved = new ResolvedSpawnPrefab(((PrefabRef)(object)prefabRef).Prefab, ((PrefabRef)(object)prefabRef).ResourcePath); return true; } catch (Exception) { resolved = default(ResolvedSpawnPrefab); return false; } } } internal sealed class RandomStagePointSelector { private static readonly FieldInfo? InStartRoomField = AccessTools.Field(typeof(LevelPoint), "inStartRoom"); internal bool TryGetPosition(float minimumPlayerDistance, HashSet excludedPointIds, out Vector3 position, out Quaternion rotation) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) position = default(Vector3); rotation = Quaternion.identity; List list; try { list = SemiFunc.LevelPointsGetAll(); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Could not collect stage points: " + ex.Message)); return false; } if (list == null || list.Count == 0) { return false; } if (TrySelect(list, minimumPlayerDistance, excludedPointIds, allowReusedPoint: false, out position, out rotation)) { return true; } if (minimumPlayerDistance > 0f && TrySelect(list, 0f, excludedPointIds, allowReusedPoint: false, out position, out rotation)) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Relaxed the requested {minimumPlayerDistance:0.#}m player distance to place a stage event."); return true; } if (TrySelect(list, minimumPlayerDistance, excludedPointIds, allowReusedPoint: true, out position, out rotation)) { return true; } if (minimumPlayerDistance > 0f) { return TrySelect(list, 0f, excludedPointIds, allowReusedPoint: true, out position, out rotation); } return false; } internal bool TryGetNavigablePosition(float minimumPlayerDistance, HashSet excludedPointIds, out Vector3 position, out Quaternion rotation) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) position = default(Vector3); rotation = Quaternion.identity; List list; try { list = SemiFunc.LevelPointsGetAll(); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Could not collect navigable stage points: " + ex.Message)); return false; } if (list == null || list.Count == 0) { return false; } if (TrySelectNavigable(list, minimumPlayerDistance, excludedPointIds, allowReusedPoint: false, out position, out rotation)) { return true; } if (minimumPlayerDistance > 0f && TrySelectNavigable(list, 0f, excludedPointIds, allowReusedPoint: false, out position, out rotation)) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Relaxed the requested {minimumPlayerDistance:0.#}m player distance to place a mine on the stage NavMesh."); return true; } if (TrySelectNavigable(list, minimumPlayerDistance, excludedPointIds, allowReusedPoint: true, out position, out rotation)) { return true; } if (minimumPlayerDistance > 0f) { return TrySelectNavigable(list, 0f, excludedPointIds, allowReusedPoint: true, out position, out rotation); } return false; } private static bool TrySelectNavigable(List candidates, float minimumPlayerDistance, HashSet excludedPointIds, bool allowReusedPoint, out Vector3 position, out Quaternion rotation) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) position = default(Vector3); rotation = Quaternion.identity; int num = Random.Range(0, candidates.Count); NavMeshHit val3 = default(NavMeshHit); RaycastHit val4 = default(RaycastHit); for (int i = 0; i < candidates.Count; i++) { LevelPoint val = candidates[(num + i) % candidates.Count]; if (IsAllowed(val, minimumPlayerDistance, excludedPointIds, allowReusedPoint)) { Vector3 zero = Vector3.zero; if (allowReusedPoint) { Vector2 insideUnitCircle = Random.insideUnitCircle; Vector2 val2 = ((Vector2)(ref insideUnitCircle)).normalized * Random.Range(0.75f, 2.25f); ((Vector3)(ref zero))..ctor(val2.x, 0f, val2.y); } if (NavMesh.SamplePosition(((Component)val).transform.position + zero, ref val3, 2.5f, -1) && Physics.Raycast(((NavMeshHit)(ref val3)).position + Vector3.up * 1.5f, Vector3.down, ref val4, 4f, -5, (QueryTriggerInteraction)1) && !(((RaycastHit)(ref val4)).normal.y < 0.65f)) { position = ((RaycastHit)(ref val4)).point + ((RaycastHit)(ref val4)).normal * 0.12f; rotation = Quaternion.FromToRotation(Vector3.up, ((RaycastHit)(ref val4)).normal); excludedPointIds.Add(((Object)val).GetInstanceID()); return true; } } } return false; } private static bool TrySelect(List candidates, float minimumPlayerDistance, HashSet excludedPointIds, bool allowReusedPoint, out Vector3 position, out Quaternion rotation) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) position = default(Vector3); rotation = Quaternion.identity; int num = Random.Range(0, candidates.Count); RaycastHit val3 = default(RaycastHit); for (int i = 0; i < candidates.Count; i++) { LevelPoint val = candidates[(num + i) % candidates.Count]; if (!IsAllowed(val, minimumPlayerDistance, excludedPointIds, allowReusedPoint)) { continue; } Vector3 zero = Vector3.zero; if (allowReusedPoint) { Vector2 insideUnitCircle = Random.insideUnitCircle; Vector2 val2 = ((Vector2)(ref insideUnitCircle)).normalized * Random.Range(0.75f, 2.25f); ((Vector3)(ref zero))..ctor(val2.x, 0f, val2.y); } if (Physics.Raycast(((Component)val).transform.position + zero + Vector3.up * 3f, Vector3.down, ref val3, 10f, -5, (QueryTriggerInteraction)1)) { if (((RaycastHit)(ref val3)).normal.y < 0.55f) { continue; } position = ((RaycastHit)(ref val3)).point + ((RaycastHit)(ref val3)).normal * 0.08f; rotation = Quaternion.FromToRotation(Vector3.up, ((RaycastHit)(ref val3)).normal); } else { position = ((Component)val).transform.position + zero + Vector3.up * 0.08f; rotation = Quaternion.identity; } excludedPointIds.Add(((Object)val).GetInstanceID()); return true; } return false; } private static bool IsAllowed(LevelPoint? point, float minimumPlayerDistance, HashSet excludedPointIds, bool allowReusedPoint) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)point == (Object)null || !((Component)point).gameObject.activeInHierarchy || (!allowReusedPoint && excludedPointIds.Contains(((Object)point).GetInstanceID())) || point.Truck || ((Object)(object)point.Room != (Object)null && (point.Room.Truck || point.Room.Extraction))) { return false; } try { object obj = InStartRoomField?.GetValue(point); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return false; } } catch { } if (minimumPlayerDistance <= 0f || (Object)(object)GameDirector.instance == (Object)null) { return true; } float num2 = minimumPlayerDistance * minimumPlayerDistance; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && ((Component)player).gameObject.activeInHierarchy) { Vector3 val = ((Component)player).transform.position - ((Component)point).transform.position; if (((Vector3)(ref val)).sqrMagnitude < num2) { return false; } } } return true; } } internal sealed class FeatherOrbEffectAdapter { private sealed class FollowOrb { internal PlayerAvatar Player { get; } internal GameObject Orb { get; } internal FollowOrb(PlayerAvatar player, GameObject orb) { Player = player; Orb = orb; } } private const float FollowOrbVerticalOffset = 0.75f; private static readonly FieldInfo? ObjectAffectedField = AccessTools.Field(typeof(ItemOrb), "objectAffected"); private static readonly FieldInfo? LocalPlayerAffectedField = AccessTools.Field(typeof(ItemOrb), "localPlayerAffected"); private readonly MonoBehaviour _coroutineOwner; private readonly VanillaEffectResolver _resolver; private readonly Dictionary _followOrbs = new Dictionary(); private readonly List _staleKeys = new List(); private readonly List _targets = new List(); private GameObject? _virtualOrb; private ItemOrb? _virtualItemOrb; internal FeatherOrbEffectAdapter(MonoBehaviour coroutineOwner, VanillaEffectResolver resolver) { _coroutineOwner = coroutineOwner; _resolver = resolver; } internal bool EnsureAvailable() { if (ObjectAffectedField != null) { return _resolver.ResolveFeather(); } return false; } internal void ApplyOnce(IReadOnlyList targets, IReadOnlyList players) { if (!EnsureAvailable()) { return; } EnsureVirtualOrb(); _targets.Clear(); HashSet hashSet = new HashSet(); foreach (PhysGrabObject target in targets) { if ((Object)(object)target != (Object)null && hashSet.Add(((Object)target).GetInstanceID())) { _targets.Add(target); } } if ((Object)(object)_virtualItemOrb != (Object)null) { try { ObjectAffectedField.SetValue(_virtualItemOrb, new List(_targets)); _virtualItemOrb.itemActive = true; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not refresh virtual Feather Orb targets: " + ex.Message)); } } SyncFollowOrbs(players); UpdateFollowOrbPositions(); } internal void AddTarget(PhysGrabObject target) { if (!EnsureAvailable() || (Object)(object)target == (Object)null || (Object)(object)((Component)target).GetComponent() != (Object)null) { return; } foreach (PhysGrabObject target2 in _targets) { if ((Object)(object)target2 == (Object)(object)target) { return; } } _targets.Add(target); EnsureVirtualOrb(); if (!((Object)(object)_virtualItemOrb != (Object)null)) { return; } try { ObjectAffectedField.SetValue(_virtualItemOrb, new List(_targets)); _virtualItemOrb.itemActive = true; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not add a new Feather target: " + ex.Message)); } } internal void TickFollowOrbs() { _staleKeys.Clear(); UpdateFollowOrbPositions(); foreach (KeyValuePair followOrb in _followOrbs) { FollowOrb value = followOrb.Value; if ((Object)(object)value.Player == (Object)null || (Object)(object)value.Orb == (Object)null) { DestroyFollowOrb(value.Orb); _staleKeys.Add(followOrb.Key); continue; } ItemBattery component = value.Orb.GetComponent(); if ((Object)(object)component != (Object)null) { component.autoDrain = false; component.batteryLife = 100f; } } foreach (int staleKey in _staleKeys) { _followOrbs.Remove(staleKey); } } internal void RefreshPlayer(PlayerAvatar player) { if (!EnsureAvailable() || (Object)(object)player == (Object)null || !((Component)player).gameObject.activeInHierarchy) { return; } if ((Object)(object)player.photonView != (Object)null) { int key = ((player.photonView.ViewID != 0) ? player.photonView.ViewID : ((Object)player).GetInstanceID()); if (_followOrbs.TryGetValue(key, out FollowOrb value)) { DestroyFollowOrb(value.Orb); _followOrbs.Remove(key); } } EnsureFollowOrb(player); UpdateFollowOrbPositions(); } internal void Stop() { if ((Object)(object)_virtualItemOrb != (Object)null) { _virtualItemOrb.itemActive = false; try { ObjectAffectedField?.SetValue(_virtualItemOrb, new List()); } catch { } } if ((Object)(object)_virtualOrb != (Object)null) { Object.Destroy((Object)(object)_virtualOrb); } _virtualOrb = null; _virtualItemOrb = null; _targets.Clear(); foreach (FollowOrb value in _followOrbs.Values) { DestroyFollowOrb(value.Orb); } _followOrbs.Clear(); } private void EnsureVirtualOrb() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_virtualOrb != (Object)null) { return; } _virtualOrb = Object.Instantiate(_resolver.FeatherPrefab, new Vector3(0f, -1000f, 0f), Quaternion.identity); ((Object)_virtualOrb).name = "StagePhysicsEvents_VirtualFeatherOrb"; ((Object)_virtualOrb).hideFlags = (HideFlags)61; _virtualItemOrb = _virtualOrb.GetComponent(); if ((Object)(object)_virtualItemOrb == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Resolved Feather Orb prefab has no ItemOrb component."); Object.Destroy((Object)(object)_virtualOrb); _virtualOrb = null; return; } _virtualItemOrb.itemActive = true; Behaviour[] componentsInChildren = _virtualOrb.GetComponentsInChildren(true); foreach (Behaviour obj in componentsInChildren) { obj.enabled = obj is ItemOrbFeather; } Renderer[] componentsInChildren2 = _virtualOrb.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { componentsInChildren2[i].enabled = false; } Collider[] componentsInChildren3 = _virtualOrb.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren3.Length; i++) { componentsInChildren3[i].enabled = false; } AudioSource[] componentsInChildren4 = _virtualOrb.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren4.Length; i++) { ((Behaviour)componentsInChildren4[i]).enabled = false; } Rigidbody[] componentsInChildren5 = _virtualOrb.GetComponentsInChildren(true); foreach (Rigidbody obj2 in componentsInChildren5) { obj2.isKinematic = true; obj2.detectCollisions = false; } } private void SyncFollowOrbs(IReadOnlyList players) { HashSet hashSet = new HashSet(); foreach (PlayerAvatar player in players) { if (!((Object)(object)player == (Object)null) && !((Object)(object)player.photonView == (Object)null) && ((Component)player).gameObject.activeInHierarchy) { int item = ((player.photonView.ViewID != 0) ? player.photonView.ViewID : ((Object)player).GetInstanceID()); hashSet.Add(item); EnsureFollowOrb(player); } } _staleKeys.Clear(); foreach (KeyValuePair followOrb in _followOrbs) { if (!hashSet.Contains(followOrb.Key) || (Object)(object)followOrb.Value.Player == (Object)null || (Object)(object)followOrb.Value.Orb == (Object)null) { DestroyFollowOrb(followOrb.Value.Orb); _staleKeys.Add(followOrb.Key); } } foreach (int staleKey in _staleKeys) { _followOrbs.Remove(staleKey); } } private void EnsureFollowOrb(PlayerAvatar player) { if ((Object)(object)player.photonView == (Object)null) { return; } int key = ((player.photonView.ViewID != 0) ? player.photonView.ViewID : ((Object)player).GetInstanceID()); if (!_followOrbs.TryGetValue(key, out FollowOrb value) || !((Object)(object)value.Orb != (Object)null)) { GameObject val = CreateFollowOrb(player); if ((Object)(object)val != (Object)null) { _followOrbs[key] = new FollowOrb(player, val); } } } private GameObject? CreateFollowOrb(PlayerAvatar player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = FollowOrbPosition(player); GameObject val2; if (SemiFunc.IsMultiplayer()) { object[] array = new object[3] { 0.01f, 0.01f, 0.01f }; val2 = PhotonNetwork.Instantiate(_resolver.FeatherResourcePath, val, Quaternion.identity, (byte)0, array); } else { val2 = Object.Instantiate(_resolver.FeatherPrefab, val, Quaternion.identity); val2.transform.localScale = Vector3.one * 0.01f; } ((Object)val2).name = "StagePhysicsEvents_PlayerFeatherOrb"; val2.AddComponent(); DisableFollowOrbPhysicalInteraction(val2); _coroutineOwner.StartCoroutine(InitializeFollowOrbAfterStart(val2, player)); return val2; } catch (Exception arg) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not create player Feather Orb: {arg}"); return null; } } private static IEnumerator InitializeFollowOrbAfterStart(GameObject orb, PlayerAvatar player) { yield return null; if ((Object)(object)orb == (Object)null || (Object)(object)player == (Object)null) { yield break; } try { ItemOrb component = orb.GetComponent(); if ((Object)(object)component == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Player Feather Orb has no ItemOrb component after initialization."); yield break; } component.targetEnemies = false; component.targetNonValuables = false; component.targetValuables = false; component.targetPlayers = true; ItemBattery component2 = orb.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.autoDrain = false; component2.batteryLife = 100f; } ItemToggle component3 = orb.GetComponent(); if ((Object)(object)component3 == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Player Feather Orb has no ItemToggle component after initialization."); yield break; } component3.ToggleItem(true, 0); DisableFollowOrbPhysicalInteraction(orb); ObjectAffectedField?.SetValue(component, new List()); bool flag = (Object)(object)PlayerController.instance?.playerAvatarScript == (Object)(object)player; LocalPlayerAffectedField?.SetValue(component, flag); component.itemActive = true; ((Behaviour)component).enabled = false; ManualLogSource modLogger = StagePhysicsEventsPlugin.ModLogger; PhotonView photonView = player.photonView; object arg = ((photonView != null) ? photonView.ViewID : 0); object arg2 = flag; PhotonView component4 = orb.GetComponent(); modLogger.LogDebug((object)$"Player Feather Orb activated: playerView={arg}, hostLocal={arg2}, orbView={((component4 != null) ? component4.ViewID : 0)}"); } catch (Exception arg3) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not initialize player Feather Orb: {arg3}"); } } private void UpdateFollowOrbPositions() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) foreach (FollowOrb value in _followOrbs.Values) { if (!((Object)(object)value.Orb == (Object)null) && !((Object)(object)value.Player == (Object)null)) { Vector3 position = FollowOrbPosition(value.Player); PhysGrabObject component = value.Orb.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.rb != (Object)null) { component.rb.isKinematic = true; component.rb.detectCollisions = false; component.rb.position = position; } value.Orb.transform.position = position; } } } private static Vector3 FollowOrbPosition(PlayerAvatar player) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) return ((Component)player).transform.position + Vector3.up * 0.75f; } private static void DisableFollowOrbPhysicalInteraction(GameObject orb) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) Collider[] componentsInChildren = orb.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = false; } PhysGrabObject val = orb.GetComponent() ?? orb.GetComponentInChildren(true); if ((Object)(object)val?.rb != (Object)null) { if (!val.rb.isKinematic) { val.rb.velocity = Vector3.zero; val.rb.angularVelocity = Vector3.zero; } val.rb.isKinematic = true; val.rb.detectCollisions = false; } } private static void DestroyFollowOrb(GameObject? orb) { if ((Object)(object)orb == (Object)null) { return; } try { ItemOrb component = orb.GetComponent(); if ((Object)(object)component != (Object)null) { component.itemActive = false; ObjectAffectedField?.SetValue(component, new List()); LocalPlayerAffectedField?.SetValue(component, false); } ItemToggle component2 = orb.GetComponent(); if (component2 != null) { component2.ToggleItem(false, 0); } DeferredObjectCleanupQueue.Enqueue(orb); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Feather Orb cleanup deferred to scene unload: " + ex.Message)); } } } internal sealed class AdditionalOrbEffectAdapter { private static readonly FieldInfo? ObjectAffectedField = AccessTools.Field(typeof(ItemOrb), "objectAffected"); private readonly MonoBehaviour _coroutineOwner; private readonly VanillaEffectResolver _resolver; private readonly StagePhysicsConfig _config; private readonly List _targets = new List(); private readonly List _healPlayers = new List(); private GameObject? _carrier; private ItemOrb? _itemOrb; private StageEffect _effect; private Coroutine? _batteryCoroutine; private Coroutine? _healCoroutine; private int _carrierGeneration; internal AdditionalOrbEffectAdapter(MonoBehaviour coroutineOwner, VanillaEffectResolver resolver, StagePhysicsConfig config) { _coroutineOwner = coroutineOwner; _resolver = resolver; _config = config; } internal static bool Supports(StageEffect effect) { if (effect != StageEffect.Battery && effect != StageEffect.Heal) { return effect == StageEffect.Indestructible; } return true; } internal bool EnsureAvailable(StageEffect effect) { GameObject prefab; string resourcePath; if (Supports(effect) && (effect == StageEffect.Heal || ObjectAffectedField != null)) { return _resolver.TryResolveOrb(effect, out prefab, out resourcePath); } return false; } internal void ApplyOnce(StageEffect effect, IReadOnlyList objects, IReadOnlyList players) { //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) if (!EnsureAvailable(effect)) { return; } if (_effect != effect) { Stop(); _effect = effect; } if (effect == StageEffect.Heal) { _healPlayers.Clear(); foreach (PlayerAvatar player in players) { if ((Object)(object)player != (Object)null && ((Component)player).gameObject.activeInHierarchy) { _healPlayers.Add(player); } } if (_healCoroutine == null) { _healCoroutine = _coroutineOwner.StartCoroutine(HealLoop()); } return; } _targets.Clear(); foreach (PhysGrabObject @object in objects) { if (!((Object)(object)@object == (Object)null) && !((Object)(object)((Component)@object).GetComponent() != (Object)null) && (effect != StageEffect.Battery || !((Object)(object)((Component)@object).GetComponent() == (Object)null))) { _targets.Add(@object); } } if (effect == StageEffect.Battery) { if (_batteryCoroutine == null) { _batteryCoroutine = _coroutineOwner.StartCoroutine(BatteryLoop()); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Battery charge loop activated with {_targets.Count} stage target(s)."); } else if ((Object)(object)_carrier == (Object)null) { CreateCarrier(effect, HiddenPosition()); } else { SetCarrierPosition(HiddenPosition()); SetTargets(); } } internal void Tick() { if (!((Object)(object)_carrier == (Object)null) && !((Object)(object)_itemOrb == (Object)null)) { _itemOrb.itemActive = true; ItemBattery component = _carrier.GetComponent(); if ((Object)(object)component != (Object)null) { component.autoDrain = false; component.batteryLife = 100f; } } } internal void AddPlayer(PlayerAvatar player) { if (_effect == StageEffect.Heal && !((Object)(object)player == (Object)null) && ((Component)player).gameObject.activeInHierarchy && !_healPlayers.Contains(player)) { _healPlayers.Add(player); if (_healCoroutine == null) { _healCoroutine = _coroutineOwner.StartCoroutine(HealLoop()); } } } internal void AddTarget(PhysGrabObject target) { if ((_effect != StageEffect.Battery && _effect != StageEffect.Indestructible) || (Object)(object)target == (Object)null || (Object)(object)((Component)target).GetComponent() != (Object)null || (_effect == StageEffect.Battery && (Object)(object)((Component)target).GetComponent() == (Object)null)) { return; } foreach (PhysGrabObject target2 in _targets) { if ((Object)(object)target2 == (Object)(object)target) { return; } } _targets.Add(target); SetTargets(); } internal void Stop() { _carrierGeneration++; if (_batteryCoroutine != null) { _coroutineOwner.StopCoroutine(_batteryCoroutine); _batteryCoroutine = null; } if (_healCoroutine != null) { _coroutineOwner.StopCoroutine(_healCoroutine); _healCoroutine = null; } _healPlayers.Clear(); _targets.Clear(); if ((Object)(object)_itemOrb != (Object)null) { _itemOrb.itemActive = false; try { ObjectAffectedField?.SetValue(_itemOrb, new List()); } catch { } } if ((Object)(object)_carrier != (Object)null) { Object.Destroy((Object)(object)_carrier); } _carrier = null; _itemOrb = null; _effect = StageEffect.None; } private void CreateCarrier(StageEffect effect, Vector3 position) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!_resolver.TryResolveOrb(effect, out GameObject prefab, out string _) || (Object)(object)prefab == (Object)null) { return; } try { _carrier = Object.Instantiate(prefab, position, Quaternion.identity); ((Object)_carrier).name = $"StagePhysicsEvents_Virtual{effect}Orb"; ((Object)_carrier).hideFlags = (HideFlags)61; _carrier.AddComponent(); Renderer[] componentsInChildren = _carrier.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = false; } Collider[] componentsInChildren2 = _carrier.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { componentsInChildren2[i].enabled = false; } AudioSource[] componentsInChildren3 = _carrier.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren3.Length; i++) { ((Behaviour)componentsInChildren3[i]).enabled = false; } DisableCarrierPhysics(_carrier); int generation = ++_carrierGeneration; _coroutineOwner.StartCoroutine(InitializeCarrierAfterStart(effect, generation)); } catch (Exception arg) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not create virtual {effect} Orb: {arg}"); Stop(); } } private IEnumerator InitializeCarrierAfterStart(StageEffect effect, int generation) { yield return null; if ((Object)(object)_carrier == (Object)null || _effect != effect || generation != _carrierGeneration) { yield break; } try { _itemOrb = _carrier.GetComponent(); Behaviour val = ResolveEffectBehaviour(_carrier, effect); if ((Object)(object)_itemOrb == (Object)null || (Object)(object)val == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Resolved {effect} Orb prefab is missing its required component."); Stop(); yield break; } Behaviour[] componentsInChildren = _carrier.GetComponentsInChildren(true); foreach (Behaviour obj in componentsInChildren) { obj.enabled = (Object)(object)obj == (Object)(object)val; } DisableCarrierPhysics(_carrier); ItemBattery component = _carrier.GetComponent(); if ((Object)(object)component != (Object)null) { component.autoDrain = false; component.batteryLife = 100f; } _itemOrb.itemActive = true; SetTargets(); StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Virtual {effect} Orb activated with {_targets.Count} stage target(s)."); } catch (Exception arg) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not initialize virtual {effect} Orb: {arg}"); Stop(); } } private IEnumerator HealLoop() { while (_effect == StageEffect.Heal) { yield return (object)new WaitForSeconds((float)_config.HealIntervalSeconds.Value); if (_effect != StageEffect.Heal) { break; } foreach (PlayerAvatar healPlayer in _healPlayers) { if (!((Object)(object)healPlayer == (Object)null) && ((Component)healPlayer).gameObject.activeInHierarchy && !((Object)(object)healPlayer.playerHealth == (Object)null)) { try { healPlayer.playerHealth.HealOther(_config.HealAmount.Value, true); } catch (Exception ex) { ManualLogSource modLogger = StagePhysicsEventsPlugin.ModLogger; PhotonView photonView = healPlayer.photonView; modLogger.LogDebug((object)$"Heal Orb target skipped for player view {((photonView != null) ? photonView.ViewID : 0)}: {ex.Message}"); } } } } _healCoroutine = null; } private IEnumerator BatteryLoop() { while (_effect == StageEffect.Battery) { yield return (object)new WaitForSeconds((float)_config.BatteryChargeIntervalSeconds.Value); if (_effect != StageEffect.Battery) { break; } float num = (float)_config.BatteryChargeAmount.Value * 10f; foreach (PhysGrabObject target in _targets) { if ((Object)(object)target == (Object)null || !((Component)target).gameObject.activeInHierarchy) { continue; } ItemBattery component = ((Component)target).GetComponent(); if (!((Object)(object)component == (Object)null) && !(component.batteryLife >= 100f)) { try { component.ChargeBattery(((Component)_coroutineOwner).gameObject, num); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Battery target skipped for " + ((Object)target).name + ": " + ex.Message)); } } } } _batteryCoroutine = null; } private void SetTargets() { if (!((Object)(object)_itemOrb == (Object)null)) { ObjectAffectedField?.SetValue(_itemOrb, new List(_targets)); _itemOrb.itemActive = true; } } private void SetCarrierPosition(Vector3 position) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_carrier == (Object)null)) { _carrier.transform.position = position; PhysGrabObject component = _carrier.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.rb != (Object)null) { component.rb.position = position; component.rb.velocity = Vector3.zero; component.rb.angularVelocity = Vector3.zero; } } } private static Vector3 HiddenPosition() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) return new Vector3(0f, -1000f, 0f); } private static Behaviour? ResolveEffectBehaviour(GameObject carrier, StageEffect effect) { return (Behaviour?)(effect switch { StageEffect.Battery => carrier.GetComponent(), StageEffect.Indestructible => carrier.GetComponent(), _ => null, }); } private static void DisableCarrierPhysics(GameObject carrier) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) Rigidbody[] componentsInChildren = carrier.GetComponentsInChildren(true); foreach (Rigidbody obj in componentsInChildren) { obj.isKinematic = true; obj.detectCollisions = false; obj.velocity = Vector3.zero; obj.angularVelocity = Vector3.zero; } } } internal sealed class VoidStaffEffectAdapter { private readonly MonoBehaviour _coroutineOwner; private readonly VanillaEffectResolver _resolver; private readonly List _spawned = new List(); private int _generation; internal VoidStaffEffectAdapter(MonoBehaviour coroutineOwner, VanillaEffectResolver resolver) { _coroutineOwner = coroutineOwner; _resolver = resolver; } internal bool EnsureAvailable() { return _resolver.ResolveVoid(); } internal void ApplyOnce(IReadOnlyList objects, IReadOnlyList players, int spawnCount) { if (EnsureAvailable() && spawnCount > 0) { Stop(); List list = CollectStagePositions(objects, players); if (list.Count == 0) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Void event found no valid stage positions."); return; } Shuffle(list); int generation = _generation; _coroutineOwner.StartCoroutine(SpawnStaged(generation, spawnCount, list)); } } private IEnumerator SpawnStaged(int generation, int spawnCount, IReadOnlyList positions) { yield return null; int created = 0; for (int index = 0; index < spawnCount; index++) { if (generation != _generation) { break; } Vector3 val = positions[index % positions.Count] + Vector3.up * 0.1f; try { GameObject val2 = (SemiFunc.IsMultiplayer() ? PhotonNetwork.Instantiate(_resolver.VoidResourcePath, val, Quaternion.identity, (byte)0, (object[])null) : Object.Instantiate(_resolver.VoidPrefab, val, Quaternion.identity)); ((Object)val2).name = "StagePhysicsEvents_Void"; val2.AddComponent(); _spawned.Add(val2); created++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not create Void Staff effect at {val}: {ex.Message}"); } yield return (object)new WaitForSeconds(0.05f); } StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Void Staff event created {created}/{spawnCount} effect(s) at random stage points."); } internal void Stop() { _generation++; DeferredObjectCleanupQueue.Enqueue((IEnumerable)_spawned); _spawned.Clear(); } private static List CollectStagePositions(IReadOnlyList objects, IReadOnlyList players) { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) List list = new List(); HashSet hashSet = new HashSet(); try { List list2 = SemiFunc.LevelPointsGetAll(); if (list2 != null) { foreach (LevelPoint item in list2) { if ((Object)(object)item != (Object)null && ((Component)item).gameObject.activeInHierarchy && hashSet.Add(((Object)item).GetInstanceID())) { list.Add(((Component)item).transform.position); } } } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Level-point collection for Void event failed: " + ex.Message)); } if (list.Count > 0) { return list; } foreach (PhysGrabObject @object in objects) { if ((Object)(object)@object != (Object)null && hashSet.Add(((Object)@object).GetInstanceID())) { list.Add(((Component)@object).transform.position); } } foreach (PlayerAvatar player in players) { if ((Object)(object)player != (Object)null && hashSet.Add(((Object)player).GetInstanceID())) { list.Add(((Component)player).transform.position); } } return list; } private static void Shuffle(IList positions) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) for (int num = positions.Count - 1; num > 0; num--) { int num2 = Random.Range(0, num + 1); int index = num; int index2 = num2; Vector3 value = positions[num2]; Vector3 value2 = positions[num]; positions[index] = value; positions[index2] = value2; } } } internal sealed class ValuableIndestructibleProtection { private readonly List _valuables = new List(); private float _startsAt; private float _endsAt; internal void Schedule(IReadOnlyList valuables, float startsAt, float endsAt) { _valuables.Clear(); HashSet hashSet = new HashSet(); foreach (PhysGrabObject valuable in valuables) { if ((Object)(object)valuable != (Object)null && (Object)(object)((Component)valuable).GetComponent() != (Object)null && hashSet.Add(((Object)valuable).GetInstanceID())) { _valuables.Add(valuable); } } _startsAt = startsAt; _endsAt = Mathf.Max(startsAt, endsAt); StagePhysicsEventsPlugin.ModLogger.LogDebug((object)($"Valuable Indestructible protection scheduled: targets={_valuables.Count}, " + $"startsIn={Mathf.Max(0f, _startsAt - Time.time):0.##}s, duration={Mathf.Max(0f, _endsAt - _startsAt):0.##}s.")); } internal void Tick() { if (_valuables.Count == 0 || Time.time < _startsAt) { return; } if (Time.time > _endsAt) { Stop(); return; } for (int num = _valuables.Count - 1; num >= 0; num--) { PhysGrabObject val = _valuables[num]; if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy) { _valuables.RemoveAt(num); } else { val.OverrideIndestructible(0.1f); } } } internal void AddTarget(PhysGrabObject valuable) { if ((Object)(object)valuable == (Object)null || (Object)(object)((Component)valuable).GetComponent() == (Object)null) { return; } foreach (PhysGrabObject valuable2 in _valuables) { if ((Object)(object)valuable2 == (Object)(object)valuable) { return; } } _valuables.Add(valuable); } internal void Stop() { _valuables.Clear(); _startsAt = 0f; _endsAt = 0f; } } internal enum StaffAffectKind { ZeroGravity, Roll } internal sealed class StaffAffectBroadcaster { private const int BatchSize = 96; private static readonly FieldInfo? SingleplayerObjectsField = AccessTools.Field(typeof(SemiAreaOfEffect), "targetPhysObjectsForSingleplayer"); private static readonly FieldInfo? SingleplayerPlayersField = AccessTools.Field(typeof(SemiAreaOfEffect), "playerAvatars"); private static readonly MethodInfo? CreateAffectsMethod = AccessTools.Method(typeof(SemiAreaOfEffect), "CreateAffectsRPC", (Type[])null, (Type[])null); private readonly MonoBehaviour _coroutineOwner; private readonly VanillaEffectResolver _resolver; private readonly StaffAffectKind _kind; private GameObject? ProjectilePrefab { get { if (_kind != StaffAffectKind.ZeroGravity) { return _resolver.RollStaffProjectilePrefab; } return _resolver.ZeroGravityProjectilePrefab; } } private GameObject? AffectPrefab { get { if (_kind != StaffAffectKind.ZeroGravity) { return _resolver.RollStaffAffectPrefab; } return _resolver.ZeroGravityAffectPrefab; } } private string? ProjectileResourcePath { get { if (_kind != StaffAffectKind.ZeroGravity) { return _resolver.RollStaffProjectileResourcePath; } return _resolver.ZeroGravityProjectileResourcePath; } } private string EffectLabel { get { if (_kind != StaffAffectKind.ZeroGravity) { return "Roll"; } return "Zero Gravity"; } } internal StaffAffectBroadcaster(MonoBehaviour coroutineOwner, VanillaEffectResolver resolver, StaffAffectKind kind) { _coroutineOwner = coroutineOwner; _resolver = resolver; _kind = kind; } internal bool EnsureAvailable() { if (_kind != StaffAffectKind.ZeroGravity) { return _resolver.ResolveRollStaff(); } return _resolver.ResolveZeroGravity(); } internal void ApplyOnce(IReadOnlyList objects, IReadOnlyList players, float durationSeconds) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) if (!EnsureAvailable() || durationSeconds <= 0f) { return; } float affectTime = Mathf.Max(0.05f, durationSeconds * ((_kind == StaffAffectKind.ZeroGravity) ? 0.5f : 1f)); float affectTime2 = Mathf.Max(0.05f, durationSeconds * 0.5f); Vector3 direction = EffectDirection(); BuildSingleplayerTargets(objects, players, out List targetObjects, out List targetPlayers); if (!SemiFunc.IsMultiplayer()) { if (_kind == StaffAffectKind.Roll) { foreach (PhysGrabObject item in targetObjects) { ApplySingleplayer((IReadOnlyList)(object)new PhysGrabObject[1] { item }, Array.Empty(), affectTime, RandomHorizontalDirection()); } ApplySingleplayer(Array.Empty(), targetPlayers, affectTime2, direction); } else { ApplySingleplayer(targetObjects, targetPlayers, affectTime, direction); } return; } HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(); foreach (PlayerAvatar item2 in targetPlayers) { if ((Object)(object)item2 != (Object)null && (Object)(object)item2.photonView != (Object)null && item2.photonView.ViewID != 0) { hashSet2.Add(item2.photonView.ViewID); } } foreach (PhysGrabObject item3 in targetObjects) { PhotonView val = ResolveTargetView(item3); if ((Object)(object)val != (Object)null && val.ViewID != 0) { hashSet.Add(val.ViewID); } } if (_kind == StaffAffectKind.Roll) { FireViewIds(hashSet, affectTime, direction, individualRandomDirections: true); FireViewIds(hashSet2, affectTime2, direction); } else { hashSet.UnionWith(hashSet2); FireViewIds(hashSet, affectTime, direction); } } private void FireViewIds(HashSet viewIds, float affectTime, Vector3 direction, bool individualRandomDirections = false) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) List list = new List(96); foreach (int viewId in viewIds) { list.Add(viewId); if (list.Count == 96) { FireBatch(list, affectTime, direction, individualRandomDirections); list.Clear(); } } if (list.Count > 0) { FireBatch(list, affectTime, direction, individualRandomDirections); } } internal void Stop() { } private void FireBatch(List viewIds, float affectTime, Vector3 direction, bool individualRandomDirections) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(0f, -1000f, 0f); GameObject val2 = PhotonNetwork.Instantiate(ProjectileResourcePath, val, Quaternion.identity, (byte)0, (object[])null); val2.AddComponent(); SlowProjectile component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } SemiAreaOfEffect componentInChildren = val2.GetComponentInChildren(true); PhotonView val3 = (((Object)(object)componentInChildren != (Object)null) ? (((Component)componentInChildren).GetComponent() ?? ((Component)componentInChildren).GetComponentInParent() ?? ((Component)componentInChildren).GetComponentInChildren(true)) : null); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)val3 == (Object)null) { PhotonNetwork.Destroy(val2); StagePhysicsEventsPlugin.ModLogger.LogWarning((object)(EffectLabel + " carrier has no accessible SemiAreaOfEffect PhotonView; RPC batch was skipped.")); return; } if (individualRandomDirections) { foreach (int viewId in viewIds) { val3.RPC("CreateAffectsRPC", (RpcTarget)0, new object[4] { new int[1] { viewId }, new float[1] { affectTime }, val, RandomHorizontalDirection() }); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"{EffectLabel} RPC batch sent: targets={viewIds.Count}, effectTimer={affectTime:0.##}s, direction=random-horizontal, view={val3.ViewID}."); } else { int[] array = viewIds.ToArray(); float[] array2 = new float[array.Length]; Array.Fill(array2, affectTime); val3.RPC("CreateAffectsRPC", (RpcTarget)0, new object[4] { array, array2, val, direction }); StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"{EffectLabel} RPC batch sent: targets={array.Length}, effectTimer={affectTime:0.##}s, direction={direction}, view={val3.ViewID}."); } _coroutineOwner.StartCoroutine(DestroyCarrierLater(val2, networked: true)); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)(EffectLabel + " RPC batch failed: " + ex.Message)); } } private void ApplySingleplayer(IReadOnlyList objects, IReadOnlyList players, float affectTime, Vector3 direction) { //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) if (SingleplayerObjectsField == null || SingleplayerPlayersField == null || CreateAffectsMethod == null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Standard singleplayer " + EffectLabel + " carrier API is unavailable; using direct vanilla SemiAffect fallback.")); ApplySingleplayerDirect(objects, players, affectTime, direction); return; } try { Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(0f, -1000f, 0f); GameObject val2 = Object.Instantiate(ProjectilePrefab, val, Quaternion.identity); val2.AddComponent(); SlowProjectile component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } SemiAreaOfEffect componentInChildren = val2.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { Object.Destroy((Object)(object)val2); StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Singleplayer " + EffectLabel + " carrier has no SemiAreaOfEffect; using direct vanilla SemiAffect fallback.")); ApplySingleplayerDirect(objects, players, affectTime, direction); return; } BuildSingleplayerTargets(objects, players, out List targetObjects, out List targetPlayers); if (targetObjects.Count == 0 && targetPlayers.Count == 0) { Object.Destroy((Object)(object)val2); return; } SingleplayerObjectsField.SetValue(componentInChildren, targetObjects); SingleplayerPlayersField.SetValue(componentInChildren, targetPlayers); componentInChildren.affectTimeMin = affectTime; componentInChildren.affectTimeMax = affectTime; componentInChildren.enemyLowestDifficultyTimeMin = affectTime; componentInChildren.enemyLowestDifficultyTimeMax = affectTime; componentInChildren.enemyHighestDifficultyTimeMin = affectTime; componentInChildren.enemyHighestDifficultyTimeMax = affectTime; CreateAffectsMethod.Invoke(componentInChildren, new object[5] { Array.Empty(), Array.Empty(), val, direction, (object)default(PhotonMessageInfo) }); StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Singleplayer {EffectLabel} carrier applied: objects={targetObjects.Count}, players={targetPlayers.Count}, effectTimer={affectTime:0.##}s."); _coroutineOwner.StartCoroutine(DestroyCarrierLater(val2, networked: false)); } catch (Exception arg) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Singleplayer {EffectLabel} carrier failed; using direct vanilla SemiAffect fallback: {arg}"); ApplySingleplayerDirect(objects, players, affectTime, direction); } } private static void BuildSingleplayerTargets(IReadOnlyList objects, IReadOnlyList players, out List targetObjects, out List targetPlayers) { targetObjects = new List(); targetPlayers = new List(); HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(); foreach (PhysGrabObject @object in objects) { if ((Object)(object)@object == (Object)null || !hashSet.Add(((Object)@object).GetInstanceID())) { continue; } PlayerAvatar componentInParent = ((Component)@object).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { if (hashSet2.Add(((Object)componentInParent).GetInstanceID())) { targetPlayers.Add(componentInParent); } } else { targetObjects.Add(@object); } } foreach (PlayerAvatar player in players) { if ((Object)(object)player != (Object)null && hashSet2.Add(((Object)player).GetInstanceID())) { targetPlayers.Add(player); } } } private void ApplySingleplayerDirect(IReadOnlyList objects, IReadOnlyList players, float affectTime, Vector3 direction) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(); foreach (PhysGrabObject @object in objects) { if (!((Object)(object)@object == (Object)null) && hashSet.Add(((Object)@object).GetInstanceID())) { PlayerAvatar componentInParent = ((Component)@object).GetComponentInParent(); SpawnSingleplayerAffect(@object, componentInParent, affectTime, direction); } } foreach (PlayerAvatar player in players) { if (!((Object)(object)player == (Object)null)) { PhysGrabObject componentInChildren = ((Component)player).GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null && hashSet.Add(((Object)componentInChildren).GetInstanceID())) { SpawnSingleplayerAffect(componentInChildren, player, affectTime, direction); } } } } private void SpawnSingleplayerAffect(PhysGrabObject target, PlayerAvatar? player, float affectTime, Vector3 direction) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = Object.Instantiate(AffectPrefab, ((Component)target).transform.position, Quaternion.identity); SemiAffect component = val.GetComponent(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val); return; } component.direction = direction; component.positionOfOriginalAreaOfEffect = ((Component)target).transform.position; component.SetupSingleplayer(((Component)target).transform, target, affectTime, player); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Singleplayer " + EffectLabel + " target skipped: " + ex.Message)); } } private Vector3 EffectDirection() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (_kind == StaffAffectKind.ZeroGravity) { return Vector3.up; } return Vector3.forward; } private static Vector3 RandomHorizontalDirection() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) float num = Random.Range(0f, 360f); return Quaternion.Euler(0f, num, 0f) * Vector3.forward; } private static PhotonView? ResolveTargetView(PhysGrabObject physObject) { PlayerAvatar componentInParent = ((Component)physObject).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { return componentInParent.photonView; } return ((Component)physObject).GetComponent() ?? ((Component)physObject).GetComponentInParent() ?? ((Component)physObject).GetComponentInChildren(true); } private static IEnumerator DestroyCarrierLater(GameObject carrier, bool networked) { yield return (object)new WaitForSeconds(1f); if (!((Object)(object)carrier == (Object)null)) { if (networked && PhotonNetwork.IsMasterClient) { PhotonNetwork.Destroy(carrier); } else if (!networked) { Object.Destroy((Object)(object)carrier); } } } } internal sealed class VanillaGrenadeEventAdapter { private readonly MonoBehaviour _coroutineOwner; private readonly VanillaSpawnEffectResolver _resolver; private readonly RandomStagePointSelector _pointSelector; private readonly StageEffect _effect; private readonly List _instances = new List(); private bool _active; private int _generation; private int _spawnCount; private int _spawnIntervalSeconds; private int _minimumPlayerDistance; private int _maximumActiveInstances; private int _launchForceMin; private int _launchForceMax; private int _pendingSpawnCount; private float _nextSpawnAt; internal VanillaGrenadeEventAdapter(MonoBehaviour coroutineOwner, VanillaSpawnEffectResolver resolver, RandomStagePointSelector pointSelector, StageEffect effect) { _coroutineOwner = coroutineOwner; _resolver = resolver; _pointSelector = pointSelector; _effect = effect; } internal bool EnsureAvailable() { ResolvedSpawnPrefab resolved; return _resolver.TryResolve(_effect, out resolved); } internal void Begin(int spawnCount, int spawnIntervalSeconds, int minimumPlayerDistance, int maximumActiveInstances, int launchForceMin, int launchForceMax) { _spawnCount = Mathf.Clamp(spawnCount, 1, 30); _spawnIntervalSeconds = Mathf.Clamp(spawnIntervalSeconds, 1, 300); _minimumPlayerDistance = Mathf.Clamp(minimumPlayerDistance, 0, 100); _maximumActiveInstances = Mathf.Clamp(maximumActiveInstances, 1, 30); _launchForceMin = Mathf.Clamp(Mathf.Min(launchForceMin, launchForceMax), 0, 100); _launchForceMax = Mathf.Clamp(Mathf.Max(launchForceMin, launchForceMax), 0, 100); if (!_active) { _active = true; _generation++; SpawnWave(_generation); _nextSpawnAt = Time.time + (float)_spawnIntervalSeconds; } } internal void Tick(float remainingSeconds) { if (_active) { PruneInstances(); if (!(remainingSeconds <= 3f) && !(Time.time < _nextSpawnAt)) { SpawnWave(_generation); _nextSpawnAt = Time.time + (float)_spawnIntervalSeconds; } } } internal void Stop() { _active = false; _generation++; DeferredObjectCleanupQueue.Enqueue((IEnumerable)_instances); _instances.Clear(); _pendingSpawnCount = 0; _nextSpawnAt = 0f; } private void SpawnWave(int generation) { if (_active && _resolver.TryResolve(_effect, out var resolved)) { PruneInstances(); int num = Mathf.Max(0, _maximumActiveInstances - _instances.Count - _pendingSpawnCount); int num2 = Mathf.Min(_spawnCount, num); if (num2 > 0) { _pendingSpawnCount += num2; _coroutineOwner.StartCoroutine(SpawnBatch(generation, num2, resolved)); } } } private IEnumerator SpawnBatch(int generation, int amount, ResolvedSpawnPrefab resolved) { yield return null; HashSet usedPoints = new HashSet(); int created = 0; for (int index = 0; index < amount; index++) { if (!_active) { break; } if (generation != _generation) { break; } if (!_pointSelector.TryGetPosition(_minimumPlayerDistance, usedPoints, out var position, out var rotation)) { _pendingSpawnCount = Mathf.Max(0, _pendingSpawnCount - (amount - index)); break; } try { GameObject val = (SemiFunc.IsMultiplayer() ? PhotonNetwork.Instantiate(resolved.ResourcePath, position, rotation, (byte)0, (object[])null) : Object.Instantiate(resolved.Prefab, position, rotation)); ((Object)val).name = $"StagePhysicsEvents_{_effect}"; val.AddComponent(); _instances.Add(val); _coroutineOwner.StartCoroutine(ArmAfterStart(val, generation)); created++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not create {_effect} grenade at {position}: {ex.Message}"); } _pendingSpawnCount = Mathf.Max(0, _pendingSpawnCount - 1); yield return (object)new WaitForSeconds(0.05f); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"{_effect} wave created {created}/{amount} grenade(s)."); } private IEnumerator ArmAfterStart(GameObject instance, int generation) { yield return null; if (!_active || generation != _generation || (Object)(object)instance == (Object)null) { DestroyInstance(instance); yield break; } ItemToggle componentInChildren = instance.GetComponentInChildren(true); ItemGrenade componentInChildren2 = instance.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren2 == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"{_effect} carrier is missing its vanilla grenade components."); DestroyInstance(instance); yield break; } try { componentInChildren.ToggleItem(true, -1); Launch(instance); ManualLogSource modLogger = StagePhysicsEventsPlugin.ModLogger; object arg = _effect; PhotonView componentInChildren3 = instance.GetComponentInChildren(true); modLogger.LogDebug((object)$"Armed event grenade: effect={arg}, view={((componentInChildren3 != null) ? componentInChildren3.ViewID : 0)}."); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not arm {_effect} grenade: {ex.Message}"); DestroyInstance(instance); } } private void Launch(GameObject instance) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) Rigidbody val = instance.GetComponentInChildren(true)?.rb ?? instance.GetComponentInChildren(true); if ((Object)(object)val == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"{_effect} grenade is missing a Rigidbody; it was armed without launch force."); return; } if (val.isKinematic) { val.isKinematic = false; } Vector3 onUnitSphere = Random.onUnitSphere; onUnitSphere.y = Mathf.Abs(onUnitSphere.y) + 0.35f; ((Vector3)(ref onUnitSphere)).Normalize(); float num = Random.Range(_launchForceMin, _launchForceMax + 1); val.AddForce(onUnitSphere * num, (ForceMode)2); StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Launched event grenade: effect={_effect}, force={num:0}, direction={onUnitSphere}."); } private void PruneInstances() { _instances.RemoveAll((GameObject instance) => (Object)(object)instance == (Object)null); } private static void DestroyInstance(GameObject? instance) { if ((Object)(object)instance == (Object)null) { return; } try { PhotonView val = instance.GetComponent() ?? instance.GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)val != (Object)null && val.ViewID != 0) { PhotonNetwork.Destroy(((Component)val).gameObject); } else { Object.Destroy((Object)(object)instance); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Grenade cleanup deferred to scene unload: " + ex.Message)); } } } internal sealed class LevitationEventAdapter { private readonly MonoBehaviour _coroutineOwner; private readonly VanillaSpawnEffectResolver _resolver; private readonly RandomStagePointSelector _pointSelector; private readonly List _sources = new List(); private bool _active; private int _generation; private int _spawnCount; private int _spawnIntervalSeconds; private int _minimumPlayerDistance; private int _maximumActiveInstances; private int _pendingSpawnCount; private float _nextSpawnAt; internal LevitationEventAdapter(MonoBehaviour coroutineOwner, VanillaSpawnEffectResolver resolver, RandomStagePointSelector pointSelector) { _coroutineOwner = coroutineOwner; _resolver = resolver; _pointSelector = pointSelector; } internal bool EnsureAvailable() { ResolvedSpawnPrefab resolved; return _resolver.TryResolve(StageEffect.Levitation, out resolved); } internal void Begin(int spawnCount, int spawnIntervalSeconds, int minimumPlayerDistance, int maximumActiveInstances) { _spawnCount = Mathf.Clamp(spawnCount, 1, 30); _spawnIntervalSeconds = Mathf.Clamp(spawnIntervalSeconds, 1, 300); _minimumPlayerDistance = Mathf.Clamp(minimumPlayerDistance, 0, 100); _maximumActiveInstances = Mathf.Clamp(maximumActiveInstances, 1, 30); if (!_active) { _active = true; _generation++; SpawnWave(_generation); _nextSpawnAt = Time.time + (float)_spawnIntervalSeconds; } } internal void Tick(float remainingSeconds) { if (_active) { PruneSources(); if (!(remainingSeconds <= 3f) && !(Time.time < _nextSpawnAt)) { SpawnWave(_generation); _nextSpawnAt = Time.time + (float)_spawnIntervalSeconds; } } } internal void Stop() { _active = false; _generation++; DeferredObjectCleanupQueue.Enqueue((IEnumerable)_sources); _sources.Clear(); _pendingSpawnCount = 0; _nextSpawnAt = 0f; } private void SpawnWave(int generation) { if (_active && _resolver.TryResolve(StageEffect.Levitation, out var resolved)) { PruneSources(); int num = Mathf.Min(_spawnCount, Mathf.Max(0, _maximumActiveInstances - _sources.Count - _pendingSpawnCount)); if (num > 0) { _pendingSpawnCount += num; _coroutineOwner.StartCoroutine(SpawnBatch(generation, num, resolved)); } } } private IEnumerator SpawnBatch(int generation, int amount, ResolvedSpawnPrefab resolved) { yield return null; HashSet usedPoints = new HashSet(); int created = 0; for (int index = 0; index < amount; index++) { if (!_active) { break; } if (generation != _generation) { break; } if (!_pointSelector.TryGetPosition(_minimumPlayerDistance, usedPoints, out var position, out var rotation)) { _pendingSpawnCount = Mathf.Max(0, _pendingSpawnCount - (amount - index)); break; } try { GameObject val = (SemiFunc.IsMultiplayer() ? PhotonNetwork.Instantiate(resolved.ResourcePath, position, rotation, (byte)0, (object[])null) : Object.Instantiate(resolved.Prefab, position, rotation)); ((Object)val).name = "StagePhysicsEvents_LevitationSource"; val.AddComponent(); _sources.Add(val); _coroutineOwner.StartCoroutine(ActivateAfterStart(val, generation)); created++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not create Levitation source at {position}: {ex.Message}"); } _pendingSpawnCount = Mathf.Max(0, _pendingSpawnCount - 1); yield return (object)new WaitForSeconds(0.05f); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Levitation wave created {created}/{amount} source(s)."); } private IEnumerator ActivateAfterStart(GameObject source, int generation) { yield return null; if (!_active || generation != _generation || (Object)(object)source == (Object)null) { DestroySource(source); yield break; } PhysGrabObjectImpactDetector componentInChildren = source.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Levitation source has no impact detector."); DestroySource(source); yield break; } Vector3 position = source.transform.position; try { switch (_resolver.LevitationActivation) { case LevitationActivationKind.ImpactLight: componentInChildren.ImpactLight(100f, position); break; case LevitationActivationKind.ImpactMedium: componentInChildren.ImpactMedium(100f, position); break; case LevitationActivationKind.ImpactHeavy: componentInChildren.ImpactHeavy(100f, position); break; case LevitationActivationKind.BreakLight: componentInChildren.BreakLight(position, true); break; case LevitationActivationKind.BreakMedium: componentInChildren.BreakMedium(position, true); break; case LevitationActivationKind.BreakHeavy: componentInChildren.BreakHeavy(position, true, 0f); break; case LevitationActivationKind.Destroy: componentInChildren.DestroyObject(false); break; default: throw new InvalidOperationException("Levitation activation event was not resolved."); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not activate Levitation source: " + ex.Message)); DestroySource(source); yield break; } yield return (object)new WaitForSeconds(0.5f); DestroySource(source); } private void PruneSources() { _sources.RemoveAll((GameObject source) => (Object)(object)source == (Object)null); } private static void DestroySource(GameObject? source) { if ((Object)(object)source == (Object)null) { return; } try { PhotonView val = source.GetComponent() ?? source.GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)val != (Object)null && val.ViewID != 0) { PhotonNetwork.Destroy(((Component)val).gameObject); } else { Object.Destroy((Object)(object)source); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Levitation source cleanup deferred to scene unload: " + ex.Message)); } } } internal sealed class GumballHypnosisEventAdapter { private readonly struct HeldTarget { internal PlayerAvatar Player { get; } internal PhysGrabObject Target { get; } internal HeldTarget(PlayerAvatar player, PhysGrabObject target) { Player = player; Target = target; } } private sealed class Carrier { internal PlayerAvatar Player { get; } internal PhysGrabObject Target { get; set; } internal GameObject Source { get; } internal GumballValuable? Gumball { get; set; } internal PhysGrabObject? PhysObject { get; set; } internal bool EffectApplied { get; set; } internal Carrier(PlayerAvatar player, PhysGrabObject target, GameObject source) { Player = player; Target = target; Source = source; } } private const float ScanIntervalSeconds = 0.1f; private const float CarrierOverrideSeconds = 0.25f; private const float CarrierScale = 0.01f; private readonly MonoBehaviour _coroutineOwner; private readonly VanillaSpawnEffectResolver _resolver; private readonly Dictionary _carriers = new Dictionary(); private readonly Dictionary _heldTargets = new Dictionary(); private readonly List _staleKeys = new List(); private bool _active; private int _generation; private int _playerCount; private float _nextScanAt; private TargetFilter _filter; internal GumballHypnosisEventAdapter(MonoBehaviour coroutineOwner, VanillaSpawnEffectResolver resolver) { _coroutineOwner = coroutineOwner; _resolver = resolver; } internal bool EnsureAvailable() { ResolvedSpawnPrefab resolved; return _resolver.TryResolve(StageEffect.GumballHypnosis, out resolved); } internal void Begin(TargetFilter filter) { Stop(); _filter = new TargetFilter(filter.Valuables, filter.CosmeticBoxes, filter.Items, doors: false, filter.Weapons, players: false, enemies: false); if (_filter.Valuables || _filter.CosmeticBoxes || _filter.Items || _filter.Weapons) { _active = true; _generation++; _playerCount = SafePlayerCount(); _nextScanAt = 0f; Tick(); } } internal void Tick() { if (_active) { int num = SafePlayerCount(); if (num != _playerCount) { ClearCarriers(); _playerCount = num; _nextScanAt = 0f; } FollowCarriers(); if (!(Time.time < _nextScanAt)) { _nextScanAt = Time.time + 0.1f; CollectHeldTargets(); SynchronizeCarriers(); } } } internal void Stop() { _active = false; _generation++; ClearCarriers(); _heldTargets.Clear(); _nextScanAt = 0f; } private void CollectHeldTargets() { _heldTargets.Clear(); foreach (PhysGrabObject item in PhysGrabObjectRegistry.Snapshot(_filter, discoverEnemies: false)) { if ((Object)(object)item == (Object)null || IsGumball(item) || item.playerGrabbing == null) { continue; } foreach (PhysGrabber item2 in item.playerGrabbing) { PlayerAvatar val = (((Object)(object)item2 != (Object)null) ? item2.playerAvatar : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.photonView == (Object)null) && ((Component)val).gameObject.activeInHierarchy) { int key = PlayerKey(val); if (!_heldTargets.ContainsKey(key)) { _heldTargets[key] = new HeldTarget(val, item); } } } } } private void SynchronizeCarriers() { _staleKeys.Clear(); foreach (KeyValuePair carrier2 in _carriers) { if (!_heldTargets.TryGetValue(carrier2.Key, out var value) || (Object)(object)carrier2.Value.Player == (Object)null || (Object)(object)carrier2.Value.Source == (Object)null) { DeactivateAndDestroy(carrier2.Value); _staleKeys.Add(carrier2.Key); } else { carrier2.Value.Target = value.Target; } } foreach (int staleKey in _staleKeys) { _carriers.Remove(staleKey); } foreach (KeyValuePair heldTarget in _heldTargets) { if (!_carriers.ContainsKey(heldTarget.Key)) { Carrier carrier = CreateCarrier(heldTarget.Value.Player, heldTarget.Value.Target); if (carrier != null) { _carriers[heldTarget.Key] = carrier; } } } } private Carrier? CreateCarrier(PlayerAvatar player, PhysGrabObject target) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) if (!_resolver.TryResolve(StageEffect.GumballHypnosis, out var resolved)) { return null; } try { Vector3 val = TargetPosition(target); GameObject val2; if (SemiFunc.IsMultiplayer()) { object[] array = new object[3] { 0.01f, 0.01f, 0.01f }; val2 = PhotonNetwork.Instantiate(resolved.ResourcePath, val, ((Component)target).transform.rotation, (byte)0, array); } else { val2 = Object.Instantiate(resolved.Prefab, val, ((Component)target).transform.rotation); val2.transform.localScale = Vector3.one * 0.01f; } val2.transform.localScale = Vector3.one * 0.01f; ((Object)val2).name = "StagePhysicsEvents_GumballHypnosisCarrier"; val2.AddComponent(); Carrier carrier = new Carrier(player, target, val2); _coroutineOwner.StartCoroutine(InitializeAfterAwake(carrier, _generation)); return carrier; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not create Gumball Hypnosis carrier: " + ex.Message)); return null; } } private IEnumerator InitializeAfterAwake(Carrier carrier, int generation) { yield return null; if (!_active || generation != _generation || (Object)(object)carrier.Source == (Object)null || (Object)(object)carrier.Player == (Object)null || (Object)(object)carrier.Target == (Object)null || !IsHeldBy(carrier.Target, carrier.Player)) { DeactivateAndDestroy(carrier); yield break; } try { carrier.Gumball = carrier.Source.GetComponentInChildren(true); carrier.PhysObject = carrier.Source.GetComponentInChildren(true); if ((Object)(object)carrier.Gumball == (Object)null || (Object)(object)carrier.PhysObject == (Object)null) { throw new InvalidOperationException("The resolved prefab has no initialized Gumball components."); } if ((Object)(object)((Component)carrier.PhysObject).GetComponent() == (Object)null) { ((Component)carrier.PhysObject).gameObject.AddComponent(); } PrepareCarrier(carrier); carrier.Gumball.PlayerStateChanged(true, carrier.Player.photonView.ViewID); carrier.EffectApplied = true; ManualLogSource modLogger = StagePhysicsEventsPlugin.ModLogger; string text = $"Gumball Hypnosis activated: playerView={carrier.Player.photonView.ViewID}, "; string name = ((Object)carrier.Target).name; PhotonView componentInChildren = carrier.Source.GetComponentInChildren(true); modLogger.LogDebug((object)(text + $"target={name}, carrierView={((componentInChildren != null) ? componentInChildren.ViewID : 0)}.")); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not initialize Gumball Hypnosis carrier: " + ex.Message)); DeactivateAndDestroy(carrier); } } private void FollowCarriers() { foreach (Carrier value in _carriers.Values) { if (!((Object)(object)value.Source == (Object)null) && !((Object)(object)value.Target == (Object)null)) { PrepareCarrier(value); } } } private static void PrepareCarrier(Carrier carrier) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)carrier.Source == (Object)null) && !((Object)(object)carrier.Target == (Object)null)) { Vector3 val = TargetPosition(carrier.Target); Quaternion rotation = ((Component)carrier.Target).transform.rotation; PhysGrabObject val2 = (carrier.PhysObject = carrier.PhysObject ?? carrier.Source.GetComponentInChildren(true)); if ((Object)(object)val2?.rb != (Object)null) { val2.OverrideKinematic(0.25f); val2.OverrideGrabDisable(0.25f); val2.OverrideIndestructible(0.25f); val2.rb.position = val; val2.rb.rotation = rotation; val2.rb.velocity = Vector3.zero; val2.rb.angularVelocity = Vector3.zero; val2.rb.detectCollisions = false; } carrier.Source.transform.SetPositionAndRotation(val, rotation); } } private void ClearCarriers() { foreach (Carrier item in new List(_carriers.Values)) { DeactivateAndDestroy(item); } _carriers.Clear(); } private static void DeactivateAndDestroy(Carrier carrier) { if ((Object)(object)carrier.Source == (Object)null) { return; } try { if (carrier.EffectApplied && (Object)(object)carrier.Gumball != (Object)null && (Object)(object)carrier.Player != (Object)null && (Object)(object)carrier.Player.photonView != (Object)null) { carrier.Gumball.PlayerStateChanged(false, carrier.Player.photonView.ViewID); } DeferredObjectCleanupQueue.Enqueue(carrier.Source); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Gumball Hypnosis cleanup deferred to scene unload: " + ex.Message)); } } private static bool IsHeldBy(PhysGrabObject target, PlayerAvatar player) { if ((Object)(object)target != (Object)null && (Object)(object)player != (Object)null && target.playerGrabbing != null) { return target.playerGrabbing.Contains(player.physGrabber); } return false; } private static bool IsGumball(PhysGrabObject target) { if (!((Object)(object)((Component)target).GetComponent() != (Object)null) && !((Object)(object)((Component)target).GetComponentInParent() != (Object)null)) { return (Object)(object)((Component)target).GetComponentInChildren(true) != (Object)null; } return true; } private static Vector3 TargetPosition(PhysGrabObject target) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target.rb != (Object)null) { return target.rb.worldCenterOfMass; } if (!(target.centerPoint != Vector3.zero)) { return ((Component)target).transform.position; } return target.centerPoint; } private static int PlayerKey(PlayerAvatar player) { if (!((Object)(object)player.photonView != (Object)null) || player.photonView.ViewID == 0) { return ((Object)player).GetInstanceID(); } return player.photonView.ViewID; } private static int SafePlayerCount() { try { return SemiFunc.PlayerGetList()?.Count ?? 0; } catch { return 0; } } } internal sealed class HealingAuraEventAdapter { private static readonly FieldInfo? TotalDurationField = AccessTools.Field(typeof(EnemyTickHealAura), "totalDuration"); private readonly MonoBehaviour _coroutineOwner; private readonly VanillaSpawnEffectResolver _resolver; private readonly RandomStagePointSelector _pointSelector; private readonly List _auras = new List(); private bool _active; private int _spawnCount; private int _healthPool; private int _spawnIntervalSeconds; private int _minimumPlayerDistance; private int _maximumActiveInstances; private int _generation; private int _pendingSpawnCount; private float _nextSpawnAt; internal HealingAuraEventAdapter(MonoBehaviour coroutineOwner, VanillaSpawnEffectResolver resolver, RandomStagePointSelector pointSelector) { _coroutineOwner = coroutineOwner; _resolver = resolver; _pointSelector = pointSelector; } internal bool EnsureAvailable() { ResolvedSpawnPrefab resolved; return _resolver.TryResolve(StageEffect.HealingAura, out resolved); } internal void Begin(int spawnCount, int healthPool, int spawnIntervalSeconds, int minimumPlayerDistance, int maximumActiveInstances) { Stop(); _spawnCount = Mathf.Clamp(spawnCount, 1, 30); _healthPool = Mathf.Clamp(healthPool, 1, 1000); _spawnIntervalSeconds = Mathf.Clamp(spawnIntervalSeconds, 1, 300); _minimumPlayerDistance = Mathf.Clamp(minimumPlayerDistance, 0, 100); _maximumActiveInstances = Mathf.Clamp(maximumActiveInstances, 1, 30); _active = true; _generation++; SpawnWave(_generation); _nextSpawnAt = Time.time + (float)_spawnIntervalSeconds; } internal void Tick(float remainingSeconds) { if (_active) { _auras.RemoveAll((GameObject aura) => (Object)(object)aura == (Object)null); if (!(remainingSeconds <= 3f) && !(Time.time < _nextSpawnAt)) { SpawnWave(_generation); _nextSpawnAt = Time.time + (float)_spawnIntervalSeconds; } } } internal void Stop() { _active = false; _generation++; DeferredObjectCleanupQueue.Enqueue((IEnumerable)_auras); _auras.Clear(); _pendingSpawnCount = 0; _nextSpawnAt = 0f; } private void SpawnWave(int generation) { if (_active && _resolver.TryResolve(StageEffect.HealingAura, out var resolved)) { _auras.RemoveAll((GameObject aura) => (Object)(object)aura == (Object)null); int num = Mathf.Min(_spawnCount, Mathf.Max(0, _maximumActiveInstances - _auras.Count - _pendingSpawnCount)); if (num > 0) { _pendingSpawnCount += num; _coroutineOwner.StartCoroutine(SpawnBatch(generation, num, resolved)); } } } private IEnumerator SpawnBatch(int generation, int amount, ResolvedSpawnPrefab resolved) { yield return null; HashSet usedPoints = new HashSet(); int created = 0; for (int index = 0; index < amount && _active && generation == _generation; index++) { if (!_pointSelector.TryGetPosition(_minimumPlayerDistance, usedPoints, out var position, out var rotation)) { _pendingSpawnCount = Mathf.Max(0, _pendingSpawnCount - (amount - index)); break; } try { GameObject val = (SemiFunc.IsMultiplayer() ? PhotonNetwork.Instantiate(resolved.ResourcePath, position, rotation, (byte)0, (object[])null) : Object.Instantiate(resolved.Prefab, position, rotation)); ((Object)val).name = "StagePhysicsEvents_HealingAura"; EnemyTickHealAura val2 = val.GetComponent() ?? val.GetComponentInChildren(true); if ((Object)(object)val2 == (Object)null) { DestroyNetworkObject(val); continue; } val2.healthPool = _healthPool; TotalDurationField?.SetValue(val2, Mathf.Max(4f, (float)_spawnIntervalSeconds + 1f)); _auras.Add(val); created++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not create Healing Aura at {position}: {ex.Message}"); } _pendingSpawnCount = Mathf.Max(0, _pendingSpawnCount - 1); yield return (object)new WaitForSeconds(0.05f); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Healing Aura wave created {created}/{amount} aura(s)."); } private static void DestroyNetworkObject(GameObject? instance) { if ((Object)(object)instance == (Object)null) { return; } try { PhotonView val = instance.GetComponent() ?? instance.GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)val != (Object)null && val.ViewID != 0) { PhotonNetwork.Destroy(((Component)val).gameObject); } else { Object.Destroy((Object)(object)instance); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Healing Aura cleanup deferred to scene unload: " + ex.Message)); } } } internal sealed class StarBarrageEventAdapter { private readonly MonoBehaviour _coroutineOwner; private readonly VanillaSpawnEffectResolver _resolver; private readonly RandomStagePointSelector _pointSelector; private readonly List _projectiles = new List(); private bool _active; private int _projectileCount; private int _spawnIntervalSeconds; private int _minimumPlayerDistance; private int _maximumActiveInstances; private int _generation; private int _pendingSpawnCount; private float _nextSpawnAt; internal StarBarrageEventAdapter(MonoBehaviour coroutineOwner, VanillaSpawnEffectResolver resolver, RandomStagePointSelector pointSelector) { _coroutineOwner = coroutineOwner; _resolver = resolver; _pointSelector = pointSelector; } internal bool EnsureAvailable() { ResolvedSpawnPrefab resolved; return _resolver.TryResolve(StageEffect.StarBarrage, out resolved); } internal void Begin(int projectileCount, int spawnIntervalSeconds, int minimumPlayerDistance, int maximumActiveInstances) { Stop(); _projectileCount = Mathf.Clamp(projectileCount, 1, 30); _spawnIntervalSeconds = Mathf.Clamp(spawnIntervalSeconds, 1, 300); _minimumPlayerDistance = Mathf.Clamp(minimumPlayerDistance, 0, 100); _maximumActiveInstances = Mathf.Clamp(maximumActiveInstances, 1, 30); _active = true; _generation++; SpawnWave(_generation); _nextSpawnAt = Time.time + (float)_spawnIntervalSeconds; } internal void Tick(float remainingSeconds) { if (_active) { _projectiles.RemoveAll((GameObject projectile) => (Object)(object)projectile == (Object)null); if (!(remainingSeconds <= 3f) && !(Time.time < _nextSpawnAt)) { SpawnWave(_generation); _nextSpawnAt = Time.time + (float)_spawnIntervalSeconds; } } } internal void Stop() { _active = false; _generation++; DeferredObjectCleanupQueue.Enqueue((IEnumerable)_projectiles); _projectiles.Clear(); _pendingSpawnCount = 0; _nextSpawnAt = 0f; } private void SpawnWave(int generation) { if (_active && _resolver.TryResolve(StageEffect.StarBarrage, out var resolved)) { _projectiles.RemoveAll((GameObject projectile) => (Object)(object)projectile == (Object)null); int num = Mathf.Min(_projectileCount, Mathf.Max(0, _maximumActiveInstances - _projectiles.Count - _pendingSpawnCount)); if (num > 0) { _pendingSpawnCount += num; _coroutineOwner.StartCoroutine(SpawnBatch(generation, num, resolved)); } } } private IEnumerator SpawnBatch(int generation, int amount, ResolvedSpawnPrefab resolved) { yield return null; HashSet usedPoints = new HashSet(); int created = 0; for (int index = 0; index < amount && _active && generation == _generation; index++) { if (!_pointSelector.TryGetPosition(_minimumPlayerDistance, usedPoints, out var position, out var _)) { _pendingSpawnCount = Mathf.Max(0, _pendingSpawnCount - (amount - index)); break; } Vector3 val = Random.onUnitSphere; val.y = Mathf.Clamp(val.y, -0.25f, 0.75f); val = ((((Vector3)(ref val)).sqrMagnitude > 0.001f) ? ((Vector3)(ref val)).normalized : Vector3.forward); position += Vector3.up; try { GameObject val2 = (SemiFunc.IsMultiplayer() ? PhotonNetwork.Instantiate(resolved.ResourcePath, position, Quaternion.LookRotation(val), (byte)0, (object[])null) : Object.Instantiate(resolved.Prefab, position, Quaternion.LookRotation(val))); ((Object)val2).name = "StagePhysicsEvents_StarBarrage"; SlowProjectile val3 = val2.GetComponent() ?? val2.GetComponentInChildren(true); if ((Object)(object)val3 == (Object)null) { DestroyNetworkObject(val2); continue; } val3.Launch(val); _projectiles.Add(val2); created++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not create Star Barrage projectile at {position}: {ex.Message}"); } _pendingSpawnCount = Mathf.Max(0, _pendingSpawnCount - 1); yield return (object)new WaitForSeconds(0.05f); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Star Barrage wave launched {created}/{amount} projectile(s)."); } private static void DestroyNetworkObject(GameObject? instance) { if ((Object)(object)instance == (Object)null) { return; } try { PhotonView val = instance.GetComponent() ?? instance.GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)val != (Object)null && val.ViewID != 0) { PhotonNetwork.Destroy(((Component)val).gameObject); } else { Object.Destroy((Object)(object)instance); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Star Barrage cleanup deferred to scene unload: " + ex.Message)); } } } internal sealed class SpiderScareEventAdapter { private readonly MonoBehaviour _coroutineOwner; private readonly VanillaSpawnEffectResolver _resolver; private readonly List _sources = new List(); private bool _active; private int _generation; private int _playersPerWave; private int _spawnIntervalSeconds; private float _nextSpawnAt; internal SpiderScareEventAdapter(MonoBehaviour coroutineOwner, VanillaSpawnEffectResolver resolver) { _coroutineOwner = coroutineOwner; _resolver = resolver; } internal bool EnsureAvailable(TargetFilter filter) { ResolvedSpawnPrefab resolved; if (filter.Players) { return _resolver.TryResolve(StageEffect.SpiderScare, out resolved); } return false; } internal void Begin(TargetFilter filter, int playersPerWave, int spawnIntervalSeconds) { Stop(); if (filter.Players) { _playersPerWave = Mathf.Clamp(playersPerWave, 1, 30); _spawnIntervalSeconds = Mathf.Clamp(spawnIntervalSeconds, 1, 300); _active = true; _generation++; SpawnWave(_generation); _nextSpawnAt = Time.time + (float)_spawnIntervalSeconds; } } internal void Tick(float remainingSeconds) { if (_active) { _sources.RemoveAll((GameObject source) => (Object)(object)source == (Object)null); if (!(remainingSeconds <= 3f) && !(Time.time < _nextSpawnAt)) { SpawnWave(_generation); _nextSpawnAt = Time.time + (float)_spawnIntervalSeconds; } } } internal void Stop() { _active = false; _generation++; DeferredObjectCleanupQueue.Enqueue((IEnumerable)_sources); _sources.Clear(); _nextSpawnAt = 0f; } private void SpawnWave(int generation) { if (_active && _resolver.TryResolve(StageEffect.SpiderScare, out var resolved)) { List list = CollectLivingPlayers(); Shuffle(list); int amount = Mathf.Min(_playersPerWave, list.Count); _coroutineOwner.StartCoroutine(SpawnBatch(generation, amount, list, resolved)); } } private IEnumerator SpawnBatch(int generation, int amount, IReadOnlyList players, ResolvedSpawnPrefab resolved) { yield return null; int created = 0; for (int index = 0; index < amount; index++) { if (!_active) { break; } if (generation != _generation) { break; } PlayerAvatar player = players[index]; try { Vector3 val = PlayerEffectCarrierUtility.Position(player); GameObject val2 = (SemiFunc.IsMultiplayer() ? PhotonNetwork.Instantiate(resolved.ResourcePath, val, Quaternion.identity, (byte)0, (object[])null) : Object.Instantiate(resolved.Prefab, val, Quaternion.identity)); ((Object)val2).name = "StagePhysicsEvents_SpiderScareSource"; val2.AddComponent(); PlayerEffectCarrierUtility.DisablePhysicalInteraction(val2); _sources.Add(val2); _coroutineOwner.StartCoroutine(ActivateAfterStart(val2, player, generation)); created++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not create Spider Scare source: " + ex.Message)); } yield return (object)new WaitForSeconds(0.05f); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Spider Scare wave targeted {created}/{amount} player position(s)."); } private IEnumerator ActivateAfterStart(GameObject source, PlayerAvatar player, int generation) { yield return null; yield return (object)new WaitForSeconds(0.2f); if (!_active || generation != _generation || (Object)(object)source == (Object)null || (Object)(object)player == (Object)null) { DestroyNetworkObject(source); yield break; } PhysGrabObjectImpactDetector componentInChildren = source.GetComponentInChildren(true); PhysGrabObject componentInChildren2 = source.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren2 == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Spider Scare source has no initialized impact detector."); DestroyNetworkObject(source); yield break; } if ((Object)(object)((Component)componentInChildren2).GetComponent() == (Object)null) { ((Component)componentInChildren2).gameObject.AddComponent(); } try { source.transform.SetPositionAndRotation(PlayerEffectCarrierUtility.Position(player), Quaternion.identity); PlayerEffectCarrierUtility.DisablePhysicalInteraction(source); componentInChildren2.OverrideKinematic(1f); componentInChildren2.OverrideGrabDisable(1f); PhotonView val = ((Component)componentInChildren).GetComponent() ?? ((Component)componentInChildren).GetComponentInParent(); if (SemiFunc.IsMultiplayer() && (Object)(object)val != (Object)null && val.ViewID != 0) { val.RPC("DestroyObjectRPC", (RpcTarget)5, new object[1] { true }); } else { componentInChildren.DestroyObjectRPC(true, default(PhotonMessageInfo)); } ManualLogSource modLogger = StagePhysicsEventsPlugin.ModLogger; PhotonView photonView = player.photonView; modLogger.LogDebug((object)$"Activated Spider Scare at player {((photonView != null) ? photonView.ViewID : 0)}."); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not activate Spider Scare: " + ex.Message)); DestroyNetworkObject(source); } } private static List CollectLivingPlayers() { List list = new List(); if ((Object)(object)GameDirector.instance == (Object)null) { return list; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (PlayerAvatarState.IsLiving(player)) { list.Add(player); } } return list; } private static void Shuffle(List players) { for (int i = 0; i < players.Count; i++) { int num = Random.Range(i, players.Count); int index = i; int index2 = num; PlayerAvatar value = players[num]; PlayerAvatar value2 = players[i]; players[index] = value; players[index2] = value2; } } private static void DestroyNetworkObject(GameObject? instance) { if ((Object)(object)instance == (Object)null) { return; } try { PhotonView val = instance.GetComponent() ?? instance.GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)val != (Object)null && val.ViewID != 0) { PhotonNetwork.Destroy(((Component)val).gameObject); } else { Object.Destroy((Object)(object)instance); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Spider Scare cleanup deferred to scene unload: " + ex.Message)); } } } internal sealed class TrafficShockEventAdapter { private const float CarrierScale = 0.01f; private static readonly FieldInfo? PlayerTumbleField = AccessTools.Field(typeof(PlayerAvatar), "tumble"); private readonly MonoBehaviour _coroutineOwner; private readonly VanillaSpawnEffectResolver _resolver; private readonly List _sources = new List(); private bool _active; private int _generation; private int _playersPerPulse; private int _pulseIntervalSeconds; private float _nextPulseAt; internal TrafficShockEventAdapter(MonoBehaviour coroutineOwner, VanillaSpawnEffectResolver resolver) { _coroutineOwner = coroutineOwner; _resolver = resolver; } internal bool EnsureAvailable(TargetFilter filter) { ResolvedSpawnPrefab resolved; if (filter.Players) { return _resolver.TryResolve(StageEffect.TrafficShock, out resolved); } return false; } internal void Begin(TargetFilter filter, int playersPerPulse, int pulseIntervalSeconds) { Stop(); if (filter.Players) { _playersPerPulse = Mathf.Clamp(playersPerPulse, 1, 30); _pulseIntervalSeconds = Mathf.Clamp(pulseIntervalSeconds, 1, 300); _active = true; _generation++; Pulse(_generation); _nextPulseAt = Time.time + (float)_pulseIntervalSeconds; } } internal void Tick(float remainingSeconds) { if (_active) { _sources.RemoveAll((GameObject source) => (Object)(object)source == (Object)null); if (!(remainingSeconds <= 3f) && !(Time.time < _nextPulseAt)) { Pulse(_generation); _nextPulseAt = Time.time + (float)_pulseIntervalSeconds; } } } internal void Stop() { _active = false; _generation++; DeferredObjectCleanupQueue.Enqueue((IEnumerable)_sources); _sources.Clear(); _nextPulseAt = 0f; } private void Pulse(int generation) { if (_active && _resolver.TryResolve(StageEffect.TrafficShock, out var resolved)) { List list = CollectLivingPlayers(); Shuffle(list); int amount = Mathf.Min(_playersPerPulse, list.Count); _coroutineOwner.StartCoroutine(SpawnBatch(generation, amount, list, resolved)); } } private IEnumerator SpawnBatch(int generation, int amount, IReadOnlyList players, ResolvedSpawnPrefab resolved) { yield return null; int created = 0; for (int index = 0; index < amount; index++) { if (!_active) { break; } if (generation != _generation) { break; } PlayerAvatar player = players[index]; try { Vector3 val = PlayerEffectCarrierUtility.Position(player); GameObject val2; if (SemiFunc.IsMultiplayer()) { object[] array = new object[3] { 0.01f, 0.01f, 0.01f }; val2 = PhotonNetwork.Instantiate(resolved.ResourcePath, val, Quaternion.identity, (byte)0, array); } else { val2 = Object.Instantiate(resolved.Prefab, val, Quaternion.identity); } val2.transform.localScale = Vector3.one * 0.01f; ((Object)val2).name = "StagePhysicsEvents_TrafficShockSource"; val2.AddComponent(); PlayerEffectCarrierUtility.DisablePhysicalInteraction(val2); _sources.Add(val2); _coroutineOwner.StartCoroutine(ActivateAfterStart(val2, player, generation)); created++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not create Traffic Shock source: " + ex.Message)); } yield return (object)new WaitForSeconds(0.05f); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Traffic Shock pulse targeted {created}/{amount} player(s)."); } private IEnumerator ActivateAfterStart(GameObject source, PlayerAvatar player, int generation) { yield return null; if (!_active || generation != _generation || (Object)(object)source == (Object)null || (Object)(object)player == (Object)null) { DestroyNetworkObject(source); yield break; } TrafficLightValuable componentInChildren = source.GetComponentInChildren(true); PhysGrabObject componentInChildren2 = source.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren2 == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Traffic Shock source has no initialized Traffic Light components."); DestroyNetworkObject(source); yield break; } if ((Object)(object)((Component)componentInChildren2).GetComponent() == (Object)null) { ((Component)componentInChildren2).gameObject.AddComponent(); } try { source.transform.position = PlayerEffectCarrierUtility.Position(player); PlayerEffectCarrierUtility.DisablePhysicalInteraction(source); componentInChildren2.OverrideKinematic(3f); componentInChildren2.OverrideGrabDisable(3f); componentInChildren2.OverrideIndestructible(3f); SetTrafficState(componentInChildren, (States)6); ShockPlayer(player); ManualLogSource modLogger = StagePhysicsEventsPlugin.ModLogger; PhotonView photonView = player.photonView; modLogger.LogDebug((object)$"Applied Traffic Shock to player {((photonView != null) ? photonView.ViewID : 0)}."); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not activate Traffic Shock: " + ex.Message)); } yield return (object)new WaitForSeconds(3f); DestroyNetworkObject(source); } private static void SetTrafficState(TrafficLightValuable traffic, States state) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) PhotonView val = ((Component)traffic).GetComponent() ?? ((Component)traffic).GetComponentInParent(); if (SemiFunc.IsMultiplayer() && (Object)(object)val != (Object)null && val.ViewID != 0) { val.RPC("SetStateRPC", (RpcTarget)0, new object[1] { state }); } else { traffic.SetStateRPC(state, default(PhotonMessageInfo)); } } private static void ShockPlayer(PlayerAvatar player) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) object? obj = PlayerTumbleField?.GetValue(player); PlayerTumble val = (PlayerTumble)((obj is PlayerTumble) ? obj : null); if ((Object)(object)val == (Object)null) { throw new InvalidOperationException("The target player has no initialized tumble controller."); } Vector3 val2 = ((Component)player).transform.localRotation * Vector3.back; Vector3 val3 = val2 * 15f; Vector3 val4 = -((Component)player).transform.right * 45f; Vector3 val5 = val2 * 10f; PhotonView val6 = ((Component)val).GetComponent() ?? ((Component)val).GetComponentInParent(); PhotonView val7 = player.photonView ?? ((Component)player).GetComponent(); if (SemiFunc.IsMultiplayer() && (Object)(object)val6 != (Object)null && val6.ViewID != 0 && (Object)(object)val7 != (Object)null && val7.ViewID != 0) { RpcTarget val8 = (RpcTarget)5; val6.RPC("TumbleRequestRPC", val8, new object[2] { true, false }); val6.RPC("TumbleForceRPC", val8, new object[1] { val3 }); val6.RPC("TumbleTorqueRPC", val8, new object[1] { val4 }); val6.RPC("TumbleOverrideTimeRPC", val8, new object[1] { 3f }); val6.RPC("ImpactHurtSetRPC", val8, new object[2] { 3f, 10 }); val7.RPC("ForceImpulseRPC", val8, new object[1] { val5 }); } else { val.TumbleRequest(true, false); val.TumbleForce(val3); val.TumbleTorque(val4); val.TumbleOverrideTime(3f); val.ImpactHurtSet(3f, 10); player.ForceImpulse(val5); } } private static List CollectLivingPlayers() { List list = new List(); if ((Object)(object)GameDirector.instance == (Object)null) { return list; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (PlayerAvatarState.IsLiving(player)) { list.Add(player); } } return list; } private static void Shuffle(List players) { for (int i = 0; i < players.Count; i++) { int num = Random.Range(i, players.Count); int index = i; int index2 = num; PlayerAvatar value = players[num]; PlayerAvatar value2 = players[i]; players[index] = value; players[index2] = value2; } } private static void DestroyNetworkObject(GameObject? instance) { if ((Object)(object)instance == (Object)null) { return; } try { PhotonView val = instance.GetComponent() ?? instance.GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)val != (Object)null && val.ViewID != 0) { PhotonNetwork.Destroy(((Component)val).gameObject); } else { Object.Destroy((Object)(object)instance); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Traffic Shock cleanup deferred to scene unload: " + ex.Message)); } } } internal sealed class FlickerEventAdapter { private const float CarrierScale = 0.01f; private readonly MonoBehaviour _coroutineOwner; private readonly VanillaSpawnEffectResolver _resolver; private readonly List _sources = new List(); private bool _active; private int _generation; private int _intervalSeconds; private int _intensityPercent; private float _nextPulseAt; internal FlickerEventAdapter(MonoBehaviour coroutineOwner, VanillaSpawnEffectResolver resolver) { _coroutineOwner = coroutineOwner; _resolver = resolver; } internal bool EnsureAvailable(TargetFilter filter) { ResolvedSpawnPrefab resolved; if (filter.Players) { return _resolver.TryResolve(StageEffect.Flicker, out resolved); } return false; } internal void Begin(TargetFilter filter, int intervalSeconds, int intensityPercent) { Stop(); if (filter.Players) { _intervalSeconds = Mathf.Clamp(intervalSeconds, 1, 300); _intensityPercent = Mathf.Clamp(intensityPercent, 1, 500); _active = true; _generation++; Pulse(_generation); _nextPulseAt = Time.time + (float)_intervalSeconds; } } internal void Tick(float remainingSeconds) { if (_active) { _sources.RemoveAll((GameObject source) => (Object)(object)source == (Object)null); if (!(remainingSeconds <= 1f) && !(Time.time < _nextPulseAt)) { Pulse(_generation); _nextPulseAt = Time.time + (float)_intervalSeconds; } } } internal void Stop() { _active = false; _generation++; DeferredObjectCleanupQueue.Enqueue((IEnumerable)_sources); _sources.Clear(); _nextPulseAt = 0f; } private void Pulse(int generation) { if (_active && _resolver.TryResolve(StageEffect.Flicker, out var resolved)) { _coroutineOwner.StartCoroutine(SpawnBatch(generation, CollectLivingPlayers(), resolved)); } } private IEnumerator SpawnBatch(int generation, IReadOnlyList players, ResolvedSpawnPrefab resolved) { yield return null; int created = 0; foreach (PlayerAvatar player in players) { if (!_active || generation != _generation) { break; } try { Vector3 val = PlayerEffectCarrierUtility.Position(player); GameObject val2; if (SemiFunc.IsMultiplayer()) { object[] array = new object[3] { 0.01f, 0.01f, 0.01f }; val2 = PhotonNetwork.Instantiate(resolved.ResourcePath, val, Quaternion.identity, (byte)0, array); } else { val2 = Object.Instantiate(resolved.Prefab, val, Quaternion.identity); } val2.transform.localScale = Vector3.one * 0.01f; ((Object)val2).name = "StagePhysicsEvents_FlickerSource"; val2.AddComponent(); PlayerEffectCarrierUtility.DisablePhysicalInteraction(val2); _sources.Add(val2); _coroutineOwner.StartCoroutine(ActivateAfterStart(val2, player, generation)); created++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not create Flicker source: " + ex.Message)); } yield return (object)new WaitForSeconds(0.05f); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Flicker pulse created {created} vanilla light source(s)."); } private IEnumerator ActivateAfterStart(GameObject source, PlayerAvatar player, int generation) { yield return null; if (!_active || generation != _generation || (Object)(object)source == (Object)null || (Object)(object)player == (Object)null) { DestroyNetworkObject(source); yield break; } TrafficLightValuable traffic = source.GetComponentInChildren(true); PhysGrabObject componentInChildren = source.GetComponentInChildren(true); if ((Object)(object)traffic == (Object)null || (Object)(object)componentInChildren == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Flicker source has no initialized Traffic Light components."); DestroyNetworkObject(source); yield break; } try { source.transform.position = PlayerEffectCarrierUtility.Position(player); PlayerEffectCarrierUtility.DisablePhysicalInteraction(source); componentInChildren.OverrideKinematic(2f); componentInChildren.OverrideGrabDisable(2f); componentInChildren.OverrideIndestructible(2f); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not activate Flicker: " + ex.Message)); DestroyNetworkObject(source); yield break; } int transitions = Mathf.Clamp(Mathf.CeilToInt((float)_intensityPercent / 100f), 1, 5); for (int index = 0; index < transitions; index++) { if (!TrySetTrafficState(traffic, (States)0)) { break; } yield return (object)new WaitForSeconds(0.06f); if (!_active || generation != _generation || (Object)(object)source == (Object)null || !TrySetTrafficState(traffic, (States)3)) { break; } yield return (object)new WaitForSeconds(0.12f); if (!_active || generation != _generation || (Object)(object)source == (Object)null || !TrySetTrafficState(traffic, (States)8)) { break; } yield return (object)new WaitForSeconds(0.08f); } DestroyNetworkObject(source); } private static bool TrySetTrafficState(TrafficLightValuable traffic, States state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) try { SetTrafficState(traffic, state); return true; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not update Flicker state: " + ex.Message)); return false; } } private static void SetTrafficState(TrafficLightValuable traffic, States state) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) PhotonView val = ((Component)traffic).GetComponent() ?? ((Component)traffic).GetComponentInParent(); if (SemiFunc.IsMultiplayer() && (Object)(object)val != (Object)null && val.ViewID != 0) { val.RPC("SetStateRPC", (RpcTarget)0, new object[1] { state }); } else { traffic.SetStateRPC(state, default(PhotonMessageInfo)); } } private static List CollectLivingPlayers() { List list = new List(); if ((Object)(object)GameDirector.instance == (Object)null) { return list; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (PlayerAvatarState.IsLiving(player)) { list.Add(player); } } return list; } private static void DestroyNetworkObject(GameObject? instance) { if ((Object)(object)instance == (Object)null) { return; } try { PhotonView val = instance.GetComponent() ?? instance.GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)val != (Object)null && val.ViewID != 0) { PhotonNetwork.Destroy(((Component)val).gameObject); } else { Object.Destroy((Object)(object)instance); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Flicker cleanup deferred to scene unload: " + ex.Message)); } } } internal sealed class DangerousValuablesEventAdapter { private sealed class DangerousInstance { internal GameObject Source { get; } internal DangerousValuableKind Kind { get; } internal LevitationActivationKind Activation { get; } internal DangerousInstance(GameObject source, DangerousValuableKind kind, LevitationActivationKind activation) { Source = source; Kind = kind; Activation = activation; } } private static readonly MethodInfo? CarUpdateStateMethod = AccessTools.Method(typeof(ValuableCar), "UpdateState", new Type[1] { typeof(State) }, (Type[])null); private static readonly MethodInfo? PlaneUpdateStateMethod = AccessTools.Method(typeof(ValuablePlane), "UpdateState", new Type[1] { typeof(State) }, (Type[])null); private readonly StagePhysicsConfig _config; private readonly List _instances = new List(); private bool _active; private int _targetCount; private float _nextReactivationAt; internal DangerousValuablesEventAdapter(StagePhysicsConfig config) { _config = config; } internal bool EnsureAvailable() { return _config.DangerousValuablesHasEnabledType; } internal void Begin(int targetCount) { Stop(); _targetCount = Mathf.Clamp(targetCount, 1, _config.DangerousValuablesMaximumActiveInstances.Value); _active = true; CollectAndActivatePlacedValuables(); _nextReactivationAt = Time.time + (float)_config.DangerousValuablesReactivationIntervalSeconds.Value; } internal void Tick(float remainingSeconds) { if (_active) { _instances.RemoveAll((DangerousInstance instance) => (Object)(object)instance.Source == (Object)null); if (!(remainingSeconds <= 3f) && !(Time.time < _nextReactivationAt)) { ReactivateExisting(); _nextReactivationAt = Time.time + (float)_config.DangerousValuablesReactivationIntervalSeconds.Value; } } } internal void Stop() { _active = false; _instances.Clear(); _nextReactivationAt = 0f; } private static void Activate(DangerousInstance instance) { if ((Object)(object)instance.Source == (Object)null) { return; } switch (instance.Kind) { case DangerousValuableKind.IceSaw: { IceSawValuable componentInChildren3 = instance.Source.GetComponentInChildren(true); if (componentInChildren3 != null) { componentInChildren3.TrapActivate(); } break; } case DangerousValuableKind.Blender: { BlenderValuable componentInChildren5 = instance.Source.GetComponentInChildren(true); if (componentInChildren5 != null) { componentInChildren5.TrapActivate(); } break; } case DangerousValuableKind.Flamethrower: { FlamethrowerValuable componentInChildren7 = instance.Source.GetComponentInChildren(true); if (componentInChildren7 != null) { componentInChildren7.GrabTrigger(); } break; } case DangerousValuableKind.Egg: { ValuableEgg componentInChildren4 = instance.Source.GetComponentInChildren(true); if ((Object)(object)componentInChildren4 != (Object)null) { SetEggState(componentInChildren4, (EggState)4); } break; } case DangerousValuableKind.Car: { ValuableCar componentInChildren8 = instance.Source.GetComponentInChildren(true); if ((Object)(object)componentInChildren8 != (Object)null) { SetCarState(componentInChildren8, (State)2); } break; } case DangerousValuableKind.Plane: { ValuablePlane componentInChildren6 = instance.Source.GetComponentInChildren(true); if ((Object)(object)componentInChildren6 != (Object)null) { SetPlaneState(componentInChildren6, (State)2); } break; } case DangerousValuableKind.Broom: { WizardBroomValuable componentInChildren = instance.Source.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.TrapActivate(); break; } PhysGrabObjectImpactDetector componentInChildren2 = instance.Source.GetComponentInChildren(true); if ((Object)(object)componentInChildren2 != (Object)null) { InvokeActivation(componentInChildren2, instance.Activation); } break; } } } private void ReactivateExisting() { foreach (DangerousInstance instance in _instances) { if ((Object)(object)instance.Source == (Object)null) { continue; } try { switch (instance.Kind) { case DangerousValuableKind.IceSaw: { IceSawValuable componentInChildren2 = instance.Source.GetComponentInChildren(true); if ((Object)(object)componentInChildren2 != (Object)null) { componentInChildren2.TrapActivate(); } break; } case DangerousValuableKind.Blender: { BlenderValuable componentInChildren4 = instance.Source.GetComponentInChildren(true); if ((Object)(object)componentInChildren4 != (Object)null) { componentInChildren4.TrapActivate(); } break; } case DangerousValuableKind.Flamethrower: { FlamethrowerValuable componentInChildren5 = instance.Source.GetComponentInChildren(true); if ((Object)(object)componentInChildren5 != (Object)null) { componentInChildren5.GrabTrigger(); } break; } case DangerousValuableKind.Car: { ValuableCar componentInChildren3 = instance.Source.GetComponentInChildren(true); if ((Object)(object)componentInChildren3 != (Object)null) { SetCarState(componentInChildren3, (State)2); } break; } case DangerousValuableKind.Plane: { ValuablePlane componentInChildren = instance.Source.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { SetPlaneState(componentInChildren, (State)2); } break; } case DangerousValuableKind.Egg: case DangerousValuableKind.Broom: Activate(instance); break; } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Could not reactivate dangerous {instance.Kind} valuable: {ex.Message}"); } } } private void CollectAndActivatePlacedValuables() { List list = FindPlacedDangerousValuables(); Shuffle(list); int num = Mathf.Max(0, Mathf.Min(_targetCount, _config.DangerousValuablesMaximumActiveInstances.Value) - _instances.Count); int num2 = 0; foreach (DangerousInstance instance in list) { if (num2 < num && !_instances.Exists((DangerousInstance current) => (Object)(object)current.Source == (Object)(object)instance.Source)) { _instances.Add(instance); try { Activate(instance); num2++; } catch (Exception ex) { _instances.Remove(instance); StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Could not activate placed dangerous {instance.Kind} valuable: {ex.Message}"); } } } StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Dangerous Valuables activated {num2} already-placed stage valuable(s)."); } private List FindPlacedDangerousValuables() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) List list = new List(); HashSet hashSet = new HashSet(); ValuableObject[] array = Resources.FindObjectsOfTypeAll(); foreach (ValuableObject val in array) { if ((Object)(object)val == (Object)null) { continue; } Scene scene = ((Component)val).gameObject.scene; if (!((Scene)(ref scene)).IsValid() || !((Component)val).gameObject.activeInHierarchy) { continue; } GameObject gameObject = ((Component)val).gameObject; if (!((Object)(object)gameObject.GetComponentInParent() != (Object)null) && !((Object)(object)gameObject.GetComponentInChildren(true) != (Object)null) && TryIdentifyKind(gameObject, out var kind) && IsKindEnabled(kind)) { int instanceID = ((Object)gameObject).GetInstanceID(); if (hashSet.Add(instanceID)) { list.Add(new DangerousInstance(gameObject, kind, LevitationActivationKind.None)); } } } return list; } private static bool TryIdentifyKind(GameObject source, out DangerousValuableKind kind) { if ((Object)(object)source.GetComponentInChildren(true) != (Object)null) { kind = DangerousValuableKind.IceSaw; return true; } if ((Object)(object)source.GetComponentInChildren(true) != (Object)null) { kind = DangerousValuableKind.Blender; return true; } if ((Object)(object)source.GetComponentInChildren(true) != (Object)null) { kind = DangerousValuableKind.Flamethrower; return true; } if ((Object)(object)source.GetComponentInChildren(true) != (Object)null) { kind = DangerousValuableKind.Egg; return true; } if ((Object)(object)source.GetComponentInChildren(true) != (Object)null) { kind = DangerousValuableKind.Car; return true; } if ((Object)(object)source.GetComponentInChildren(true) != (Object)null) { kind = DangerousValuableKind.Plane; return true; } if ((Object)(object)source.GetComponentInChildren(true) != (Object)null) { kind = DangerousValuableKind.Broom; return true; } kind = DangerousValuableKind.IceSaw; return false; } private static void SetEggState(ValuableEgg egg, EggState state) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) PhotonView val = ((Component)egg).GetComponent() ?? ((Component)egg).GetComponentInParent(); if (SemiFunc.IsMultiplayer() && (Object)(object)val != (Object)null && val.ViewID != 0) { val.RPC("SetStateRPC", (RpcTarget)0, new object[1] { state }); } else { egg.SetStateRPC(state, default(PhotonMessageInfo)); } } private static void SetCarState(ValuableCar car, State state) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) PhotonView val = ((Component)car).GetComponent() ?? ((Component)car).GetComponentInParent(); if (SemiFunc.IsMultiplayer() && (Object)(object)val != (Object)null && val.ViewID != 0) { val.RPC("UpdateStateRPC", (RpcTarget)0, new object[1] { state }); } else if (CarUpdateStateMethod != null) { CarUpdateStateMethod.Invoke(car, new object[1] { state }); } } private static void SetPlaneState(ValuablePlane plane, State state) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected I4, but got Unknown PhotonView val = ((Component)plane).GetComponent() ?? ((Component)plane).GetComponentInParent(); if (SemiFunc.IsMultiplayer() && (Object)(object)val != (Object)null && val.ViewID != 0) { val.RPC("UpdateStateRPC", (RpcTarget)0, new object[1] { (int)state }); } else if (PlaneUpdateStateMethod != null) { PlaneUpdateStateMethod.Invoke(plane, new object[1] { state }); } } private bool IsKindEnabled(DangerousValuableKind kind) { return kind switch { DangerousValuableKind.IceSaw => _config.DangerousValuablesIceSawEnabled.Value, DangerousValuableKind.Blender => _config.DangerousValuablesBlenderEnabled.Value, DangerousValuableKind.Flamethrower => _config.DangerousValuablesFlamethrowerEnabled.Value, DangerousValuableKind.Egg => _config.DangerousValuablesEggEnabled.Value, DangerousValuableKind.Car => _config.DangerousValuablesCarEnabled.Value, DangerousValuableKind.Plane => _config.DangerousValuablesPlaneEnabled.Value, DangerousValuableKind.Broom => _config.DangerousValuablesBroomEnabled.Value, _ => false, }; } private static void InvokeActivation(PhysGrabObjectImpactDetector detector, LevitationActivationKind activation) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)detector).transform.position; switch (activation) { case LevitationActivationKind.ImpactLight: detector.ImpactLight(100f, position); break; case LevitationActivationKind.ImpactMedium: detector.ImpactMedium(100f, position); break; case LevitationActivationKind.ImpactHeavy: detector.ImpactHeavy(100f, position); break; case LevitationActivationKind.BreakLight: detector.BreakLight(position, true); break; case LevitationActivationKind.BreakMedium: detector.BreakMedium(position, true); break; case LevitationActivationKind.BreakHeavy: detector.BreakHeavy(position, true, 0f); break; case LevitationActivationKind.Destroy: detector.DestroyObject(false); break; default: throw new InvalidOperationException("No synchronized Broom activation was resolved."); } } private static void Shuffle(List list) { for (int i = 0; i < list.Count; i++) { int num = Random.Range(i, list.Count); int index = i; int index2 = num; T value = list[num]; T value2 = list[i]; list[index] = value; list[index2] = value2; } } } internal sealed class MinefieldEventAdapter { private static readonly FieldInfo? MineStateField = AccessTools.Field(typeof(ItemMine), "state"); private readonly MonoBehaviour _coroutineOwner; private readonly VanillaSpawnEffectResolver _resolver; private readonly RandomStagePointSelector _pointSelector; private readonly StagePhysicsConfig _config; private readonly List _instances = new List(); private bool _active; private int _generation; private int _targetCount; private int _pendingSpawnCount; private float _nextReplenishAt; internal MinefieldEventAdapter(MonoBehaviour coroutineOwner, VanillaSpawnEffectResolver resolver, RandomStagePointSelector pointSelector, StagePhysicsConfig config) { _coroutineOwner = coroutineOwner; _resolver = resolver; _pointSelector = pointSelector; _config = config; } internal bool EnsureAvailable() { IReadOnlyList prefabs; if (_config.MinefieldHasEnabledType) { return _resolver.TryResolveMines(_config, out prefabs); } return false; } internal void Begin(int targetCount) { _targetCount = Mathf.Clamp(targetCount, 1, _config.MinefieldMaximumActiveMines.Value); if (!_active) { _active = true; _generation++; Replenish(_generation); _nextReplenishAt = Time.time + (float)_config.MinefieldReplenishIntervalSeconds.Value; } } internal void Tick(float remainingSeconds) { if (_active) { PruneInstances(); if (_config.MinefieldReplenishTriggeredMines.Value && !(remainingSeconds <= 3f) && !(Time.time < _nextReplenishAt)) { Replenish(_generation); _nextReplenishAt = Time.time + (float)_config.MinefieldReplenishIntervalSeconds.Value; } } } internal void Stop() { _active = false; _generation++; foreach (GameObject instance in _instances) { if (HasStartedDetonation(instance)) { continue; } GameObject queuedMine = instance; DeferredObjectCleanupQueue.Enqueue((Action)delegate { if (!HasStartedDetonation(queuedMine)) { DestroyInstance(queuedMine); } }); } _instances.Clear(); _pendingSpawnCount = 0; _nextReplenishAt = 0f; } private void Replenish(int generation) { if (_active && _resolver.TryResolveMines(_config, out IReadOnlyList prefabs)) { int num = ActiveMineCount() + _pendingSpawnCount; int num2 = Mathf.Clamp(_config.MinefieldMaximumActiveMines.Value, 1, 30); int num3 = Mathf.Min(Mathf.Max(0, _targetCount - num), Mathf.Max(0, num2 - num)); if (num3 > 0) { _pendingSpawnCount += num3; _coroutineOwner.StartCoroutine(SpawnBatch(generation, num3, prefabs)); StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Minefield scheduled {num3} staged mine spawn(s); target={_targetCount}."); } } } private IEnumerator SpawnBatch(int generation, int amount, IReadOnlyList prefabs) { yield return null; HashSet usedPoints = new HashSet(); int created = 0; for (int index = 0; index < amount; index++) { if (!_active) { break; } if (generation != _generation) { break; } if (!_pointSelector.TryGetNavigablePosition(_config.MinefieldMinimumPlayerDistance.Value, usedPoints, out var position, out var rotation)) { _pendingSpawnCount = Mathf.Max(0, _pendingSpawnCount - (amount - index)); break; } ResolvedMinePrefab resolvedMinePrefab = prefabs[Random.Range(0, prefabs.Count)]; try { GameObject val = (SemiFunc.IsMultiplayer() ? PhotonNetwork.Instantiate(resolvedMinePrefab.SpawnPrefab.ResourcePath, position, rotation, (byte)0, (object[])null) : Object.Instantiate(resolvedMinePrefab.SpawnPrefab.Prefab, position, rotation)); ((Object)val).name = $"StagePhysicsEvents_Minefield_{resolvedMinePrefab.MineType}"; val.AddComponent(); _instances.Add(val); _coroutineOwner.StartCoroutine(ArmAfterStart(val, generation, position, rotation)); created++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not create {resolvedMinePrefab.MineType} mine at {position}: {ex.Message}"); } _pendingSpawnCount = Mathf.Max(0, _pendingSpawnCount - 1); yield return (object)new WaitForSeconds(0.08f); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Minefield staged spawn created {created}/{amount} mine(s); target={_targetCount}."); } private IEnumerator ArmAfterStart(GameObject instance, int generation, Vector3 placementPosition, Quaternion placementRotation) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) yield return null; yield return null; if (!_active || generation != _generation || (Object)(object)instance == (Object)null) { DestroyInstance(instance); yield break; } ItemMine componentInChildren = instance.GetComponentInChildren(true); ItemToggle componentInChildren2 = instance.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren2 == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Minefield carrier has no initialized ItemMine or ItemToggle component."); DestroyInstance(instance); yield break; } try { PhysGrabObject componentInChildren3 = instance.GetComponentInChildren(true); if ((Object)(object)componentInChildren3 != (Object)null) { componentInChildren3.Teleport(placementPosition, placementRotation); if ((Object)(object)componentInChildren3.rb != (Object)null) { componentInChildren3.rb.velocity = Vector3.zero; componentInChildren3.rb.angularVelocity = Vector3.zero; } componentInChildren3.OverrideKinematic(5f); componentInChildren3.OverrideGrabDisable(2f); } componentInChildren.triggeredByForces = false; componentInChildren2.ToggleItem(true, -1); PhotonView val = ((Component)componentInChildren).GetComponent() ?? ((Component)componentInChildren).GetComponentInParent(); if (SemiFunc.IsMultiplayer() && (Object)(object)val != (Object)null && val.ViewID != 0) { val.RPC("StateSetRPC", (RpcTarget)0, new object[1] { 2 }); } else { componentInChildren.StateSetRPC(2, default(PhotonMessageInfo)); } ManualLogSource modLogger = StagePhysicsEventsPlugin.ModLogger; string text = $"Placed stable armed event mine: type={componentInChildren.mineType}, "; PhotonView componentInChildren4 = instance.GetComponentInChildren(true); modLogger.LogDebug((object)(text + $"view={((componentInChildren4 != null) ? componentInChildren4.ViewID : 0)}, " + $"position={placementPosition}.")); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not arm event mine: " + ex.GetBaseException().Message)); DestroyInstance(instance); } } private int ActiveMineCount() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Invalid comparison between Unknown and I4 //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Invalid comparison between Unknown and I4 int num = 0; foreach (GameObject instance in _instances) { if ((Object)(object)instance == (Object)null) { continue; } ItemMine componentInChildren = instance.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { continue; } try { States val = (States)((!(MineStateField?.GetValue(componentInChildren) is States val2)) ? 2 : ((int)val2)); if ((int)val != 5 && (int)val != 4) { num++; } } catch { num++; } } return num; } private void PruneInstances() { _instances.RemoveAll((GameObject instance) => (Object)(object)instance == (Object)null); } private static bool HasStartedDetonation(GameObject? instance) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 if ((Object)(object)instance == (Object)null) { return false; } ItemMine componentInChildren = instance.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { return false; } try { States val = (States)((MineStateField?.GetValue(componentInChildren) is States val2) ? ((int)val2) : 0); return (int)val == 4 || (int)val == 5; } catch { return false; } } private static void DestroyInstance(GameObject? instance) { if ((Object)(object)instance == (Object)null) { return; } try { PhotonView val = instance.GetComponent() ?? instance.GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)val != (Object)null && val.ViewID != 0) { PhotonNetwork.Destroy(((Component)val).gameObject); } else { Object.Destroy((Object)(object)instance); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Mine cleanup deferred to scene unload: " + ex.Message)); } } } internal sealed class EnemyWaveEventAdapter { private static readonly MethodInfo? SpawnMethod = AccessTools.Method(typeof(EnemyParent), "Spawn", (Type[])null, (Type[])null); private static readonly FieldInfo? SetupDoneField = AccessTools.Field(typeof(EnemyParent), "SetupDone"); private static readonly FieldInfo? SpawnedField = AccessTools.Field(typeof(EnemyParent), "Spawned"); private static readonly FieldInfo? EnemyField = AccessTools.Field(typeof(EnemyParent), "Enemy"); private readonly MonoBehaviour _coroutineOwner; private readonly StagePhysicsConfig _config; private readonly Dictionary _leasedEnemies = new Dictionary(); private bool _active; private int _generation; private int _targetCount; private int _pendingSpawnCount; private float _nextReplenishAt; internal EnemyWaveEventAdapter(MonoBehaviour coroutineOwner, StagePhysicsConfig config) { _coroutineOwner = coroutineOwner; _config = config; } internal bool EnsureAvailable() { if ((Object)(object)EnemyDirector.instance != (Object)null) { return SpawnMethod != null; } return false; } internal void Begin(int targetCount) { _targetCount = Mathf.Clamp(targetCount, 1, 30); if (!_active) { _active = true; _generation++; Replenish(_generation); _nextReplenishAt = Time.time + (float)_config.EnemyWaveReplenishIntervalSeconds.Value; } } internal void Tick(float remainingSeconds) { if (_active) { PruneLeases(); if (!(remainingSeconds <= 3f) && !(Time.time < _nextReplenishAt)) { Replenish(_generation); _nextReplenishAt = Time.time + (float)_config.EnemyWaveReplenishIntervalSeconds.Value; } } } internal void Stop() { _active = false; _generation++; if (_config.EnemyWaveDespawnOnEnd.Value) { foreach (EnemyParent value in _leasedEnemies.Values) { if ((Object)(object)value == (Object)null || !IsSpawned(value)) { continue; } EnemyParent queuedEnemy = value; DeferredObjectCleanupQueue.Enqueue((Action)delegate { if ((Object)(object)queuedEnemy != (Object)null && IsSpawned(queuedEnemy)) { queuedEnemy.Despawn(); } }); } } _leasedEnemies.Clear(); _pendingSpawnCount = 0; _nextReplenishAt = 0f; } private void Replenish(int generation) { if (!_active || SpawnMethod == null) { return; } PruneLeases(); int num = Mathf.Max(0, _targetCount - _leasedEnemies.Count - _pendingSpawnCount); if (num != 0) { List list = CollectDormantEnemies(); num = Mathf.Min(num, list.Count); if (num != 0) { _pendingSpawnCount += num; _coroutineOwner.StartCoroutine(SpawnStaged(generation, num, list)); } } } private IEnumerator SpawnStaged(int generation, int amount, List candidates) { int created = 0; for (int index = 0; index < amount; index++) { if (candidates.Count <= 0) { break; } if (!_active) { break; } if (generation != _generation) { break; } int index2 = Random.Range(0, candidates.Count); EnemyParent val = candidates[index2]; candidates.RemoveAt(index2); try { SpawnMethod.Invoke(val, Array.Empty()); int key = EnemyKey(val); _leasedEnemies[key] = val; _coroutineOwner.StartCoroutine(PlaceAfterSpawn(val, generation)); created++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not activate Enemy Wave enemy " + val.enemyName + ": " + ex.GetBaseException().Message)); } _pendingSpawnCount = Mathf.Max(0, _pendingSpawnCount - 1); yield return null; } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Enemy Wave activated {created}/{amount} additional enemy/enemies; target={_targetCount}."); } private IEnumerator PlaceAfterSpawn(EnemyParent enemyParent, int generation) { yield return (object)new WaitForSeconds(0.5f); Enemy val = (((Object)(object)enemyParent != (Object)null) ? GetEnemy(enemyParent) : null); if (!_active || generation != _generation || (Object)(object)enemyParent == (Object)null || (Object)(object)val == (Object)null || !IsSpawned(enemyParent)) { yield break; } try { val.TeleportToPoint((float)_config.EnemyWaveMinimumPlayerDistance.Value, (float)Mathf.Max(_config.EnemyWaveMinimumPlayerDistance.Value + 10, 100)); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Enemy Wave kept the vanilla spawn point for " + enemyParent.enemyName + ": " + ex.Message)); } } private List CollectDormantEnemies() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) List list = new List(); EnemyParent[] array = Resources.FindObjectsOfTypeAll(); foreach (EnemyParent val in array) { if (!((Object)(object)val == (Object)null)) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid() && IsSetupDone(val) && !IsSpawned(val) && !_leasedEnemies.ContainsKey(EnemyKey(val))) { list.Add(val); } } } return list; } private void PruneLeases() { foreach (int item in new List(_leasedEnemies.Keys)) { if (!_leasedEnemies.TryGetValue(item, out EnemyParent value) || (Object)(object)value == (Object)null || !IsSpawned(value)) { _leasedEnemies.Remove(item); } } } private static bool IsSetupDone(EnemyParent enemyParent) { try { object obj = SetupDoneField?.GetValue(enemyParent); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return num == 0 || flag; } catch { return true; } } private static bool IsSpawned(EnemyParent enemyParent) { try { object obj = SpawnedField?.GetValue(enemyParent); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private static Enemy? GetEnemy(EnemyParent enemyParent) { try { object? obj = EnemyField?.GetValue(enemyParent); return (Enemy?)((obj is Enemy) ? obj : null); } catch { return null; } } private static int EnemyKey(EnemyParent enemyParent) { if (!((Object)(object)((MonoBehaviourPun)enemyParent).photonView != (Object)null) || ((MonoBehaviourPun)enemyParent).photonView.ViewID == 0) { return ((Object)enemyParent).GetInstanceID(); } return ((MonoBehaviourPun)enemyParent).photonView.ViewID; } } internal sealed class EnemyPlayerEventAdapter { private static readonly FieldInfo? EnemyHealthCurrentField = AccessTools.Field(typeof(EnemyHealth), "healthCurrent"); private static readonly FieldInfo? EnemyHealthDeadField = AccessTools.Field(typeof(EnemyHealth), "dead"); private static readonly FieldInfo? EnemyParentSpawnedField = AccessTools.Field(typeof(EnemyParent), "Spawned"); private static readonly FieldInfo? PlayerDeadSetField = AccessTools.Field(typeof(PlayerAvatar), "deadSet"); private static readonly FieldInfo? PlayerSpectatingField = AccessTools.Field(typeof(PlayerAvatar), "spectating"); private static readonly FieldInfo? PlayerHealthValueField = AccessTools.Field(typeof(PlayerHealth), "health"); private static readonly FieldInfo? PlayerTumbleField = AccessTools.Field(typeof(PlayerAvatar), "tumble"); private static readonly FieldInfo? PlayerDeathHeadField = AccessTools.Field(typeof(PlayerAvatar), "playerDeathHead"); private readonly StagePhysicsConfig _config; private readonly Dictionary _leasedEnemies = new Dictionary(); private readonly Dictionary _playerAlive = new Dictionary(); private readonly Dictionary _playerRevives = new Dictionary(); private readonly Dictionary _playerReviveReadyAt = new Dictionary(); private StageEffect _effects; private TargetFilter _filter; private float _nextEnemyLeaseAt; private float _nextWarpAt; private float _nextRegenAt; private float _nextPurgeAt; private float _nextDamageAt; private float _nextReviveCheckAt; private float _nextKnockbackAt; private float _nextSpeedRefreshAt; private float _secondChanceFailureGraceUntil; internal EnemyPlayerEventAdapter(StagePhysicsConfig config) { _config = config; } internal bool EnsureAvailable(StageEffect effect, TargetFilter filter) { switch (effect) { case StageEffect.Freeze: case StageEffect.Stun: case StageEffect.EnemyWarp: case StageEffect.EnemyRegen: case StageEffect.EnemyPurge: case StageEffect.EnemySpeedUp: case StageEffect.EnemySpeedDown: return filter.Enemies; case StageEffect.DamagePulse: case StageEffect.SecondChance: case StageEffect.Knockback: return filter.Players; default: return false; } } internal void Begin(StageEffect effects, TargetFilter filter) { Stop(); _effects = effects; _filter = filter; _nextSpeedRefreshAt = (_nextKnockbackAt = (_nextReviveCheckAt = (_nextDamageAt = (_nextPurgeAt = (_nextRegenAt = (_nextWarpAt = (_nextEnemyLeaseAt = Time.time))))))) + 1f; _secondChanceFailureGraceUntil = 0f; CapturePlayerStates(); EnemySpeedRuntime.SetMultiplier(Has(StageEffect.EnemySpeedUp) ? ((float)_config.EnemySpeedUpPercent.Value / 100f) : (Has(StageEffect.EnemySpeedDown) ? ((float)_config.EnemySpeedDownPercent.Value / 100f) : 1f)); Tick(); } internal void Tick() { if (_effects != StageEffect.None) { float time = Time.time; if (time >= _nextEnemyLeaseAt && (Has(StageEffect.Freeze) || Has(StageEffect.Stun))) { RefreshEnemyLeases(); _nextEnemyLeaseAt = time + 1f; } if (Has(StageEffect.EnemyWarp) && time >= _nextWarpAt) { WarpEnemies(); _nextWarpAt = time + (float)_config.EnemyWarpIntervalSeconds.Value; } if (Has(StageEffect.EnemyRegen) && time >= _nextRegenAt) { RegenerateEnemies(); _nextRegenAt = time + (float)_config.EnemyRegenIntervalSeconds.Value; } if (Has(StageEffect.EnemyPurge) && time >= _nextPurgeAt) { DamageEnemies(); _nextPurgeAt = time + (float)_config.EnemyPurgeIntervalSeconds.Value; } if (Has(StageEffect.DamagePulse) && time >= _nextDamageAt) { DamagePlayers(); _nextDamageAt = time + (float)_config.DamagePulseIntervalSeconds.Value; } if (Has(StageEffect.SecondChance) && time >= _nextReviveCheckAt) { ReviveNewlyDeadPlayers(); _nextReviveCheckAt = time + (float)_config.SecondChanceCheckIntervalSeconds.Value; } if (Has(StageEffect.Knockback) && time >= _nextKnockbackAt) { KnockbackPlayers(); _nextKnockbackAt = time + (float)_config.KnockbackIntervalSeconds.Value; } if ((Has(StageEffect.EnemySpeedUp) || Has(StageEffect.EnemySpeedDown)) && time >= _nextSpeedRefreshAt) { EnemySpeedRuntime.RefreshExistingEnemies(); _nextSpeedRefreshAt = time + 1f; } } } internal void Stop() { if (Has(StageEffect.Freeze) || Has(StageEffect.Stun)) { foreach (Enemy value in _leasedEnemies.Values) { if ((Object)(object)value == (Object)null) { continue; } try { if (Has(StageEffect.Freeze)) { value.Freeze(0f); } EnemyStateStunned componentInChildren = ((Component)value).GetComponentInChildren(true); if (Has(StageEffect.Stun) && (Object)(object)componentInChildren != (Object)null) { componentInChildren.Reset(); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Enemy lease cleanup failed: " + ex.Message)); } } } _effects = StageEffect.None; EnemySpeedRuntime.Clear(); _leasedEnemies.Clear(); _playerAlive.Clear(); _playerRevives.Clear(); _playerReviveReadyAt.Clear(); _secondChanceFailureGraceUntil = 0f; } private bool Has(StageEffect effect) { return StageEffectSet.Contains(_effects, effect); } private void RefreshEnemyLeases() { foreach (Enemy item in ActiveEnemies()) { try { if (Has(StageEffect.Freeze)) { item.Freeze(2f); } EnemyStateStunned componentInChildren = ((Component)item).GetComponentInChildren(true); if (Has(StageEffect.Stun) && (Object)(object)componentInChildren != (Object)null) { componentInChildren.Set(2f); } _leasedEnemies[EnemyKey(item)] = item; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Enemy state lease failed for " + ((Object)item).name + ": " + ex.Message)); } } } private void WarpEnemies() { List list = ActiveEnemies(); Shuffle(list); int num = Mathf.Min(_config.EnemyWarpEnemiesPerPulse.Value, list.Count); for (int i = 0; i < num; i++) { try { list[i].TeleportToPoint((float)_config.EnemyWarpMinimumPlayerDistance.Value, 100f); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Enemy Warp skipped " + ((Object)list[i]).name + ": " + ex.Message)); } } } private void RegenerateEnemies() { foreach (Enemy item in ActiveEnemies()) { EnemyHealth componentInChildren = ((Component)item).GetComponentInChildren(true); if (!((Object)(object)componentInChildren == (Object)null) && !IsEnemyDead(componentInChildren)) { int num = ReadEnemyHealth(componentInChildren); int num2 = Mathf.Min(_config.EnemyRegenHealAmount.Value, Mathf.Max(0, componentInChildren.health - num)); if (num2 > 0) { componentInChildren.Heal(num2); } } } } private void DamageEnemies() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) foreach (Enemy item in ActiveEnemies()) { EnemyHealth componentInChildren = ((Component)item).GetComponentInChildren(true); if (!((Object)(object)componentInChildren == (Object)null) && !IsEnemyDead(componentInChildren)) { int num = _config.EnemyPurgeDamageAmount.Value; if (!_config.EnemyPurgeCanKill.Value) { num = Mathf.Min(num, Mathf.Max(0, ReadEnemyHealth(componentInChildren) - 1)); } if (num > 0) { componentInChildren.Hurt(num, Vector3.up); } } } } private void DamagePlayers() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) int num = 0; foreach (PlayerAvatar item in LivingPlayers()) { try { item.playerHealth.HurtOther(_config.DamagePulseAmount.Value, Vector3.zero, _config.DamagePulseSavingGrace.Value, -1, false); num++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Damage Pulse skipped a player: " + ex.Message)); } } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Damage Pulse requested {_config.DamagePulseAmount.Value} damage for {num} player(s)."); } private void CapturePlayerStates() { _playerAlive.Clear(); _playerRevives.Clear(); _playerReviveReadyAt.Clear(); foreach (PlayerAvatar item in AllPlayers()) { int key = PlayerKey(item); _playerAlive[key] = IsPlayerAlive(item); _playerRevives[key] = 0; } } internal bool TryReviveForLevelFailure() { if (!Has(StageEffect.SecondChance)) { return false; } if (!HasPendingRevive()) { _secondChanceFailureGraceUntil = 0f; return false; } if (_secondChanceFailureGraceUntil <= 0f) { _secondChanceFailureGraceUntil = Time.time + 5f; } if (ReviveNewlyDeadPlayers() > 0) { _secondChanceFailureGraceUntil = 0f; return true; } return Time.time < _secondChanceFailureGraceUntil; } private int ReviveNewlyDeadPlayers() { int num = 0; HashSet hashSet = new HashSet(); foreach (PlayerAvatar item in AllPlayers()) { int num2 = PlayerKey(item); hashSet.Add(num2); bool flag = IsPlayerAlive(item); bool value; bool num3 = _playerAlive.TryGetValue(num2, out value) && value; int value2; int num4 = (_playerRevives.TryGetValue(num2, out value2) ? value2 : 0); bool flag2 = false; if (num3 && !flag && num4 < _config.SecondChanceMaxRevivesPerPlayer.Value) { if (!_playerReviveReadyAt.TryGetValue(num2, out var value3)) { value3 = Time.time + 2f; _playerReviveReadyAt[num2] = value3; ManualLogSource modLogger = StagePhysicsEventsPlugin.ModLogger; PhotonView photonView = item.photonView; modLogger.LogDebug((object)$"Second Chance scheduled player view={((photonView != null) ? photonView.ViewID : 0)} for revival in 2 seconds."); } object? obj = PlayerDeathHeadField?.GetValue(item); PlayerDeathHead val = (PlayerDeathHead)((obj is PlayerDeathHead) ? obj : null); bool flag3 = val != null && (Object)(object)val != (Object)null; if (Time.time < value3 || !flag3) { flag2 = true; _playerAlive[num2] = true; continue; } try { item.Revive(false); flag = IsPlayerAlive(item); if (flag) { _playerRevives[num2] = num4 + 1; _playerReviveReadyAt.Remove(num2); num++; ManualLogSource modLogger2 = StagePhysicsEventsPlugin.ModLogger; PhotonView photonView2 = item.photonView; modLogger2.LogInfo((object)$"Second Chance revived player view={((photonView2 != null) ? photonView2.ViewID : 0)} ({num4 + 1}/{_config.SecondChanceMaxRevivesPerPlayer.Value})."); } else { flag2 = true; StagePhysicsEventsPlugin.ModLogger.LogDebug((object)"Second Chance is waiting for the player's vanilla death head to initialize."); } } catch (Exception ex) { flag2 = true; StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Second Chance revive failed: " + ex.Message)); } } _playerAlive[num2] = flag2 || flag; if (!_playerRevives.ContainsKey(num2)) { _playerRevives[num2] = 0; } } foreach (int item2 in new List(_playerAlive.Keys)) { if (!hashSet.Contains(item2)) { _playerAlive.Remove(item2); _playerRevives.Remove(item2); _playerReviveReadyAt.Remove(item2); } } return num; } private bool HasPendingRevive() { foreach (PlayerAvatar item in AllPlayers()) { int key = PlayerKey(item); bool flag = IsPlayerAlive(item); bool value; bool num = _playerAlive.TryGetValue(key, out value) && value; int value2; int num2 = (_playerRevives.TryGetValue(key, out value2) ? value2 : 0); if (num && !flag && num2 < _config.SecondChanceMaxRevivesPerPlayer.Value) { return true; } } return false; } private void KnockbackPlayers() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) int num = 0; Vector3 val = default(Vector3); foreach (PlayerAvatar item in LivingPlayers()) { Vector2 insideUnitCircle = Random.insideUnitCircle; Vector2 normalized = ((Vector2)(ref insideUnitCircle)).normalized; ((Vector3)(ref val))..ctor(normalized.x * (float)_config.KnockbackHorizontalForce.Value, (float)_config.KnockbackVerticalForce.Value, normalized.y * (float)_config.KnockbackHorizontalForce.Value); val *= 2f; object? obj = PlayerTumbleField?.GetValue(item); PlayerTumble val2 = (PlayerTumble)((obj is PlayerTumble) ? obj : null); if ((Object)(object)val2 != (Object)null) { val2.TumbleRequest(true, false); val2.TumbleForce(val); val2.TumbleOverrideTime(1.25f); } item.ForceImpulse(val); num++; } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Knockback applied a tumbling impulse to {num} player(s)."); } private static List ActiveEnemies() { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) List list = new List(); Enemy[] array = Resources.FindObjectsOfTypeAll(); bool flag = default(bool); foreach (Enemy val in array) { EnemyParent val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInParent() : null); EnemyHealth val3 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponentInChildren(true) : null); int num3; if ((Object)(object)val2 != (Object)null) { object obj = EnemyParentSpawnedField?.GetValue(val2); int num2; if (obj is bool) { flag = (bool)obj; num2 = 1; } else { num2 = 0; } num3 = ((num2 == 0 || flag) ? 1 : 0); } else { num3 = 0; } bool flag2 = (byte)num3 != 0; int num4; if ((Object)(object)val != (Object)null) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { num4 = (((Component)val).gameObject.activeInHierarchy ? 1 : 0); goto IL_00ad; } } num4 = 0; goto IL_00ad; IL_00ad: if (((uint)num4 & (flag2 ? 1u : 0u)) != 0 && ((Object)(object)val3 == (Object)null || !IsEnemyDead(val3))) { list.Add(val); } } return list; } private static List AllPlayers() { List list = new List(); if ((Object)(object)GameDirector.instance == (Object)null) { return list; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player != (Object)null) { list.Add(player); } } return list; } private static List LivingPlayers() { List list = new List(); foreach (PlayerAvatar item in AllPlayers()) { if (IsPlayerAlive(item)) { list.Add(item); } } return list; } private static bool IsPlayerAlive(PlayerAvatar player) { if ((Object)(object)player.playerHealth == (Object)null) { return false; } object obj = PlayerDeadSetField?.GetValue(player); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return false; } obj = PlayerSpectatingField?.GetValue(player); bool flag2 = default(bool); int num2; if (obj is bool) { flag2 = (bool)obj; num2 = 1; } else { num2 = 0; } if (((uint)num2 & (flag2 ? 1u : 0u)) != 0) { return false; } if (PlayerHealthValueField?.GetValue(player.playerHealth) is int num3) { return num3 > 0; } return true; } private static bool IsEnemyDead(EnemyHealth health) { object obj = EnemyHealthDeadField?.GetValue(health); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static int ReadEnemyHealth(EnemyHealth health) { object obj = EnemyHealthCurrentField?.GetValue(health); if (obj is int) { return (int)obj; } return health.health; } private static int EnemyKey(Enemy enemy) { if (!((Object)(object)((MonoBehaviourPun)enemy).photonView != (Object)null) || ((MonoBehaviourPun)enemy).photonView.ViewID == 0) { return ((Object)enemy).GetInstanceID(); } return ((MonoBehaviourPun)enemy).photonView.ViewID; } private static int PlayerKey(PlayerAvatar player) { if (!((Object)(object)player.photonView != (Object)null) || player.photonView.ViewID == 0) { return ((Object)player).GetInstanceID(); } return player.photonView.ViewID; } private static void Shuffle(List list) { for (int i = 0; i < list.Count; i++) { int num = Random.Range(i, list.Count); int index = i; int index2 = num; T value = list[num]; T value2 = list[i]; list[index] = value; list[index2] = value2; } } } internal sealed class EnemyHuntEventAdapter { private const float CarrierScale = 0.01f; private const float ActiveStateRefreshSeconds = 0.5f; private readonly MonoBehaviour _coroutineOwner; private readonly VanillaSpawnEffectResolver _resolver; private bool _active; private int _generation; private int _intervalSeconds; private float _nextPulseAt; private float _nextStateRefreshAt; private GameObject? _source; private ScreamDollValuable? _screamDoll; private PhysGrabObject? _physObject; internal EnemyHuntEventAdapter(MonoBehaviour coroutineOwner, VanillaSpawnEffectResolver resolver) { _coroutineOwner = coroutineOwner; _resolver = resolver; } internal bool EnsureAvailable(TargetFilter filter) { ResolvedSpawnPrefab resolved; if (filter.Enemies && AllExtractionsCompleted()) { return _resolver.TryResolve(StageEffect.EnemyHunt, out resolved); } return false; } internal void Begin(int intervalSeconds) { Stop(); _intervalSeconds = Mathf.Clamp(intervalSeconds, 1, 300); _active = true; _generation++; _nextPulseAt = Time.time; _nextStateRefreshAt = 0f; } internal void Tick(float remainingSeconds) { if (!_active || remainingSeconds <= 1f || !AllExtractionsCompleted()) { return; } if (Time.time >= _nextPulseAt) { Pulse(_generation); _nextPulseAt = Time.time + (float)_intervalSeconds; } if ((Object)(object)_source != (Object)null && (Object)(object)_screamDoll != (Object)null && Time.time >= _nextStateRefreshAt) { if ((Object)(object)_physObject != (Object)null) { _physObject.grabbed = true; _physObject.OverrideKinematic(1f); _physObject.OverrideGrabDisable(1f); _physObject.OverrideIndestructible(1f); } SetScreamState(_screamDoll, (States)1); _nextStateRefreshAt = Time.time + 0.5f; } } internal void Stop() { _active = false; _generation++; if ((Object)(object)_screamDoll != (Object)null) { try { SetScreamState(_screamDoll, (States)0); } catch { } } DestroyNetworkObject(_source); _source = null; _screamDoll = null; _physObject = null; _nextPulseAt = 0f; _nextStateRefreshAt = 0f; } private void Pulse(int generation) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) if (!_active || !_resolver.TryResolve(StageEffect.EnemyHunt, out var resolved) || !TryGetPlayerRoomPoint(out var position)) { return; } if ((Object)(object)EnemyDirector.instance != (Object)null) { EnemyDirector.instance.SetInvestigate(position, 100f, false); } DestroyNetworkObject(_source); _source = null; _screamDoll = null; _physObject = null; try { object[] array = new object[3] { 0.01f, 0.01f, 0.01f }; _source = (SemiFunc.IsMultiplayer() ? PhotonNetwork.Instantiate(resolved.ResourcePath, position, Quaternion.identity, (byte)0, array) : Object.Instantiate(resolved.Prefab, position, Quaternion.identity)); _source.transform.localScale = Vector3.one * 0.01f; ((Object)_source).name = "StagePhysicsEvents_EnemyHuntSound"; _source.AddComponent(); _coroutineOwner.StartCoroutine(ActivateAfterStart(_source, generation)); StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Enemy Hunt emitted a synchronized lure in a player room at {position}."); } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not create Enemy Hunt room sound: " + ex.Message)); DestroyNetworkObject(_source); _source = null; } } private IEnumerator ActivateAfterStart(GameObject source, int generation) { yield return null; if (!_active || generation != _generation || (Object)(object)source == (Object)null) { DestroyNetworkObject(source); yield break; } _screamDoll = source.GetComponentInChildren(true); _physObject = source.GetComponentInChildren(true); if ((Object)(object)_screamDoll == (Object)null || (Object)(object)_physObject == (Object)null) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)"Enemy Hunt sound source has no initialized Scream Doll components."); DestroyNetworkObject(source); _source = null; yield break; } _physObject.grabbed = true; _physObject.OverrideKinematic(1f); _physObject.OverrideGrabDisable(1f); _physObject.OverrideIndestructible(1f); SetScreamState(_screamDoll, (States)1); _nextStateRefreshAt = Time.time + 0.5f; } private static bool TryGetPlayerRoomPoint(out Vector3 position) { //IL_0001: 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) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) position = default(Vector3); try { List list = SemiFunc.LevelPointsGetInPlayerRooms(); if (list != null && list.Count > 0) { LevelPoint val = list[Random.Range(0, list.Count)]; if ((Object)(object)val != (Object)null) { position = ((Component)val).transform.position + Vector3.up * 0.25f; return true; } } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Enemy Hunt could not query player-room points: " + ex.Message)); } if ((Object)(object)GameDirector.instance == (Object)null) { return false; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (PlayerAvatarState.IsLiving(player)) { position = ((Component)player).transform.position; return true; } } return false; } private static bool AllExtractionsCompleted() { if ((Object)(object)RoundDirector.instance != (Object)null) { return RoundDirector.instance.allExtractionPointsCompleted; } return false; } private static void SetScreamState(ScreamDollValuable screamDoll, States state) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) PhotonView val = ((Component)screamDoll).GetComponent() ?? ((Component)screamDoll).GetComponentInParent(); if (SemiFunc.IsMultiplayer() && (Object)(object)val != (Object)null && val.ViewID != 0) { val.RPC("SetStateRPC", (RpcTarget)0, new object[1] { state }); } else { screamDoll.SetStateRPC(state, default(PhotonMessageInfo)); } } private static void DestroyNetworkObject(GameObject? instance) { if ((Object)(object)instance == (Object)null) { return; } try { PhotonView val = instance.GetComponent() ?? instance.GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)val != (Object)null && val.ViewID != 0) { PhotonNetwork.Destroy(((Component)val).gameObject); } else { Object.Destroy((Object)(object)instance); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Enemy Hunt sound cleanup deferred to scene unload: " + ex.Message)); } } } internal static class EnemySpeedRuntime { private readonly struct NavValues { internal EnemyNavMeshAgent Agent { get; } internal float DefaultSpeed { get; } internal float DefaultAcceleration { get; } internal NavValues(EnemyNavMeshAgent agent, float defaultSpeed, float defaultAcceleration) { Agent = agent; DefaultSpeed = defaultSpeed; DefaultAcceleration = defaultAcceleration; } } private readonly struct RigidbodyValues { internal EnemyRigidbody Agent { get; } internal float PositionSpeedIdle { get; } internal float PositionSpeedChase { get; } internal float RotationSpeedIdle { get; } internal float RotationSpeedChase { get; } internal RigidbodyValues(EnemyRigidbody agent, float positionSpeedIdle, float positionSpeedChase, float rotationSpeedIdle, float rotationSpeedChase) { Agent = agent; PositionSpeedIdle = positionSpeedIdle; PositionSpeedChase = positionSpeedChase; RotationSpeedIdle = rotationSpeedIdle; RotationSpeedChase = rotationSpeedChase; } } private static readonly FieldInfo? DefaultSpeedField = AccessTools.Field(typeof(EnemyNavMeshAgent), "DefaultSpeed"); private static readonly FieldInfo? DefaultAccelerationField = AccessTools.Field(typeof(EnemyNavMeshAgent), "DefaultAcceleration"); private static readonly Dictionary NavOriginals = new Dictionary(); private static readonly Dictionary RigidbodyOriginals = new Dictionary(); private static readonly Dictionary OverrideFrames = new Dictionary(); private static float _multiplier = 1f; internal static bool Active => !Mathf.Approximately(_multiplier, 1f); internal static void SetMultiplier(float multiplier) { _multiplier = Mathf.Clamp(multiplier, 0.1f, 5f); if (Active) { var (num, num2) = RefreshExistingEnemies(); StagePhysicsEventsPlugin.ModLogger.LogInfo((object)($"Enemy movement multiplier set to {_multiplier:0.##}x for " + $"{num} NavMesh and {num2} Rigidbody agent(s).")); } } internal static (int NavCount, int RigidbodyCount) RefreshExistingEnemies() { if (!Active || !SemiFunc.IsMasterClientOrSingleplayer()) { return (NavCount: 0, RigidbodyCount: 0); } int num = 0; EnemyNavMeshAgent[] array = Resources.FindObjectsOfTypeAll(); for (int i = 0; i < array.Length; i++) { if (ApplyNav(array[i])) { num++; } } int num2 = 0; EnemyRigidbody[] array2 = Resources.FindObjectsOfTypeAll(); for (int i = 0; i < array2.Length; i++) { if (ApplyRigidbody(array2[i])) { num2++; } } return (NavCount: num, RigidbodyCount: num2); } internal static void ApplyNewNav(EnemyNavMeshAgent enemyAgent) { if (Active && SemiFunc.IsMasterClientOrSingleplayer()) { ApplyNav(enemyAgent); } } internal static void ApplyNewRigidbody(EnemyRigidbody enemyRigidbody) { if (Active && SemiFunc.IsMasterClientOrSingleplayer()) { ApplyRigidbody(enemyRigidbody); } } internal static void ApplyNavOverride(EnemyNavMeshAgent enemyAgent, ref float speed, ref float acceleration) { if (CanApply((Component)(object)enemyAgent) && TryEnterOverride((Component)(object)enemyAgent, 1)) { speed = Mathf.Max(0.1f, speed * _multiplier); acceleration = Mathf.Max(0f, acceleration * _multiplier); } } internal static void ApplyRigidbodyOverride(EnemyRigidbody enemyRigidbody, ref float speed, int methodKind) { if (CanApply((Component)(object)enemyRigidbody) && TryEnterOverride((Component)(object)enemyRigidbody, methodKind)) { speed = Mathf.Max(0.1f, speed * _multiplier); } } internal static void Clear() { bool active = Active; _multiplier = 1f; foreach (NavValues value in NavOriginals.Values) { EnemyNavMeshAgent agent = value.Agent; if (!((Object)(object)agent == (Object)null)) { DefaultSpeedField?.SetValue(agent, value.DefaultSpeed); DefaultAccelerationField?.SetValue(agent, value.DefaultAcceleration); NavMeshAgent component = ((Component)agent).GetComponent(); if ((Object)(object)component != (Object)null) { component.speed = value.DefaultSpeed; component.acceleration = value.DefaultAcceleration; } } } foreach (RigidbodyValues value2 in RigidbodyOriginals.Values) { EnemyRigidbody agent2 = value2.Agent; if (!((Object)(object)agent2 == (Object)null)) { agent2.positionSpeedIdle = value2.PositionSpeedIdle; agent2.positionSpeedChase = value2.PositionSpeedChase; agent2.rotationSpeedIdle = value2.RotationSpeedIdle; agent2.rotationSpeedChase = value2.RotationSpeedChase; } } NavOriginals.Clear(); RigidbodyOriginals.Clear(); OverrideFrames.Clear(); if (active) { StagePhysicsEventsPlugin.ModLogger.LogInfo((object)"Enemy NavMesh and Rigidbody movement restored to vanilla."); } } private static bool ApplyNav(EnemyNavMeshAgent enemyAgent) { if (!CanApply((Component)(object)enemyAgent)) { return false; } int instanceID = ((Object)enemyAgent).GetInstanceID(); NavMeshAgent component = ((Component)enemyAgent).GetComponent(); if (!NavOriginals.TryGetValue(instanceID, out var value)) { float defaultSpeed = ((DefaultSpeedField?.GetValue(enemyAgent) is float num) ? num : (((Object)(object)component != (Object)null) ? component.speed : 0.1f)); float defaultAcceleration = ((DefaultAccelerationField?.GetValue(enemyAgent) is float num2) ? num2 : (((Object)(object)component != (Object)null) ? component.acceleration : 0f)); value = new NavValues(enemyAgent, defaultSpeed, defaultAcceleration); NavOriginals[instanceID] = value; } float num3 = Mathf.Max(0.1f, value.DefaultSpeed * _multiplier); float num4 = Mathf.Max(0f, value.DefaultAcceleration * _multiplier); DefaultSpeedField?.SetValue(enemyAgent, num3); DefaultAccelerationField?.SetValue(enemyAgent, num4); if ((Object)(object)component != (Object)null) { component.speed = num3; component.acceleration = num4; } return true; } private static bool ApplyRigidbody(EnemyRigidbody enemyRigidbody) { if (!CanApply((Component)(object)enemyRigidbody)) { return false; } int instanceID = ((Object)enemyRigidbody).GetInstanceID(); if (!RigidbodyOriginals.TryGetValue(instanceID, out var value)) { value = new RigidbodyValues(enemyRigidbody, enemyRigidbody.positionSpeedIdle, enemyRigidbody.positionSpeedChase, enemyRigidbody.rotationSpeedIdle, enemyRigidbody.rotationSpeedChase); RigidbodyOriginals[instanceID] = value; } enemyRigidbody.positionSpeedIdle = Mathf.Max(0.1f, value.PositionSpeedIdle * _multiplier); enemyRigidbody.positionSpeedChase = Mathf.Max(0.1f, value.PositionSpeedChase * _multiplier); enemyRigidbody.rotationSpeedIdle = Mathf.Max(0.1f, value.RotationSpeedIdle * _multiplier); enemyRigidbody.rotationSpeedChase = Mathf.Max(0.1f, value.RotationSpeedChase * _multiplier); return true; } private static bool CanApply(Component component) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (Active && SemiFunc.IsMasterClientOrSingleplayer() && (Object)(object)component != (Object)null && (Object)(object)component.gameObject != (Object)null) { Scene scene = component.gameObject.scene; return ((Scene)(ref scene)).IsValid(); } return false; } private static bool TryEnterOverride(Component owner, int methodKind) { int frameCount = Time.frameCount; long key = ((long)((Object)owner).GetInstanceID() << 8) ^ (uint)methodKind; if (OverrideFrames.TryGetValue(key, out var value) && value == frameCount) { return false; } OverrideFrames[key] = frameCount; return true; } } internal static class EnemySpeedPatches { [HarmonyPostfix] [HarmonyPatch(typeof(EnemyNavMeshAgent), "Awake")] private static void EnemyNavMeshAgentAwakePostfix(EnemyNavMeshAgent __instance) { EnemySpeedRuntime.ApplyNewNav(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(EnemyNavMeshAgent), "OnEnable")] private static void EnemyNavMeshAgentOnEnablePostfix(EnemyNavMeshAgent __instance) { EnemySpeedRuntime.ApplyNewNav(__instance); } [HarmonyPrefix] [HarmonyPatch(typeof(EnemyNavMeshAgent), "OverrideAgent")] private static void EnemyNavMeshAgentOverrideAgentPrefix(EnemyNavMeshAgent __instance, ref float speed, ref float acceleration) { EnemySpeedRuntime.ApplyNavOverride(__instance, ref speed, ref acceleration); } [HarmonyPostfix] [HarmonyPatch(typeof(EnemyRigidbody), "Awake")] private static void EnemyRigidbodyAwakePostfix(EnemyRigidbody __instance) { EnemySpeedRuntime.ApplyNewRigidbody(__instance); } [HarmonyPrefix] [HarmonyPatch(typeof(EnemyRigidbody), "OverrideFollowPosition")] private static void EnemyRigidbodyOverrideFollowPositionPrefix(EnemyRigidbody __instance, ref float speed) { EnemySpeedRuntime.ApplyRigidbodyOverride(__instance, ref speed, 2); } [HarmonyPrefix] [HarmonyPatch(typeof(EnemyRigidbody), "OverrideFollowRotation")] private static void EnemyRigidbodyOverrideFollowRotationPrefix(EnemyRigidbody __instance, ref float speed) { EnemySpeedRuntime.ApplyRigidbodyOverride(__instance, ref speed, 3); } } internal sealed class StageWorldEventAdapter { private sealed class SavedValuable { internal ValuableObject Valuable { get; } internal float OriginalValue { get; } internal float AppliedValue { get; } internal float Multiplier { get; } internal SavedValuable(ValuableObject valuable, float originalValue, float appliedValue, float multiplier) { Valuable = valuable; OriginalValue = originalValue; AppliedValue = appliedValue; Multiplier = multiplier; } } private static readonly MethodInfo? HingeOpenMethod = AccessTools.Method(typeof(PhysGrabHinge), "OpenImpulse", (Type[])null, (Type[])null); private static readonly MethodInfo? HingeCloseMethod = AccessTools.Method(typeof(PhysGrabHinge), "CloseImpulse", (Type[])null, (Type[])null); private static readonly FieldInfo? HingeJointField = AccessTools.Field(typeof(PhysGrabHinge), "joint"); private static readonly FieldInfo? HingePhysObjectField = AccessTools.Field(typeof(PhysGrabHinge), "physGrabObject"); private static readonly FieldInfo? ValuableCurrentValueField = AccessTools.Field(typeof(ValuableObject), "dollarValueCurrent"); private static readonly FieldInfo? ValuableValueSetField = AccessTools.Field(typeof(ValuableObject), "dollarValueSet"); private readonly StagePhysicsConfig _config; private readonly Dictionary _savedValuables = new Dictionary(); private readonly List _doorChaosHinges = new List(); private readonly HashSet _fragilityTargets = new HashSet(); private StageEffect _effects; private TargetFilter _filter; private TargetFilter _doorChaosFilter; private bool _doorChaosOpenNext; private float _nextQuakeAt; private float _nextDoorChaosAt; private float _nextValueScanAt; internal StageWorldEventAdapter(StagePhysicsConfig config) { _config = config; } internal bool EnsureAvailable(StageEffect effect, TargetFilter filter) { switch (effect) { case StageEffect.Quake: return filter.Players || filter.Valuables || filter.CosmeticBoxes || filter.Items || filter.Weapons; case StageEffect.DoorChaos: return true; case StageEffect.ValueSurge: case StageEffect.ValueCrash: return filter.Valuables; case StageEffect.Fragility: return filter.Valuables; default: return false; } } internal void Begin(StageEffect effects, TargetFilter filter) { Stop(); _effects = effects; _filter = filter; _doorChaosFilter = (Has(StageEffect.DoorChaos) ? filter.WithDoors(doors: true) : filter); _nextValueScanAt = (_nextDoorChaosAt = (_nextQuakeAt = Time.time)); if (Has(StageEffect.DoorChaos)) { CaptureDoorChaosTargets(); } Tick(); } internal void Tick() { if (_effects != StageEffect.None) { float time = Time.time; if (Has(StageEffect.Quake) && time >= _nextQuakeAt) { ApplyQuake(); _nextQuakeAt = time + (float)_config.QuakeIntervalSeconds.Value; } if (Has(StageEffect.DoorChaos) && time >= _nextDoorChaosAt) { ApplyDoorChaos(); _nextDoorChaosAt = time + (float)_config.DoorChaosIntervalSeconds.Value; } if ((Has(StageEffect.ValueSurge) || Has(StageEffect.ValueCrash)) && time >= _nextValueScanAt) { ApplyValueChangeToNewValuables(); _nextValueScanAt = time + 1f; } if (Has(StageEffect.Fragility)) { ApplyFragility((float)_config.FragilityMultiplierPercent.Value / 100f); } } } internal void Stop() { if ((Has(StageEffect.ValueSurge) && _config.ValueSurgeRestoreOnEnd.Value) || (Has(StageEffect.ValueCrash) && _config.ValueCrashRestoreOnEnd.Value)) { foreach (SavedValuable value in _savedValuables.Values) { if (!((Object)(object)value.Valuable != (Object)null)) { continue; } SavedValuable queued = value; DeferredObjectCleanupQueue.Enqueue((Action)delegate { if ((Object)(object)queued.Valuable != (Object)null) { SetValuableValue(queued.Valuable, CalculateRestoredValue(queued)); } }); } } if (Has(StageEffect.Fragility)) { foreach (PhysGrabObject fragilityTarget in _fragilityTargets) { PhysGrabObject queued2 = fragilityTarget; DeferredObjectCleanupQueue.Enqueue((Action)delegate { if ((Object)(object)queued2 != (Object)null) { queued2.OverrideFragility(1f); } }); } } _effects = StageEffect.None; _doorChaosFilter = default(TargetFilter); _savedValuables.Clear(); _doorChaosHinges.Clear(); _fragilityTargets.Clear(); } private bool Has(StageEffect effect) { return StageEffectSet.Contains(_effects, effect); } private void ApplyFragility(float multiplier) { foreach (PhysGrabObject item in PhysGrabObjectRegistry.Snapshot(new TargetFilter(valuables: true, cosmeticBoxes: false, items: false, doors: false, weapons: false, players: false, enemies: false))) { if ((Object)(object)item != (Object)null) { item.OverrideFragility(multiplier); _fragilityTargets.Add(item); } } } private void ApplyQuake() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) float num = _config.QuakeForce.Value; if (_filter.Players && (Object)(object)GameDirector.instance != (Object)null) { foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && ((Component)player).gameObject.activeInHierarchy) { Vector2 insideUnitCircle = Random.insideUnitCircle; Vector2 normalized = ((Vector2)(ref insideUnitCircle)).normalized; player.ForceImpulse(new Vector3(normalized.x * num, num * 0.35f, normalized.y * num)); } } } foreach (PhysGrabObject item in PhysGrabObjectRegistry.Snapshot(_filter)) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item.rb == (Object)null) && !item.grabbed && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null)) { Vector3 val = Random.onUnitSphere * num; val.y = Mathf.Abs(val.y) * 0.5f; item.rb.AddForce(val, (ForceMode)2); item.rb.AddTorque(Random.onUnitSphere * num, (ForceMode)2); } } } private void ApplyDoorChaos() { _doorChaosHinges.RemoveAll((PhysGrabHinge hinge) => (Object)(object)hinge == (Object)null || !((Component)hinge).gameObject.activeInHierarchy); if (_doorChaosHinges.Count == 0) { CaptureDoorChaosTargets(); } bool doorChaosOpenNext = _doorChaosOpenNext; int num = 0; int num2 = 0; foreach (PhysGrabHinge doorChaosHinge in _doorChaosHinges) { try { bool flag = IsLargeMapDoor(doorChaosHinge); DriveHinge(doorChaosHinge, doorChaosOpenNext, flag, _config.DoorChaosForce.Value); num++; if (flag) { num2++; } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Door Chaos skipped " + ((Object)doorChaosHinge).name + ": " + ex.GetBaseException().Message)); } } _doorChaosOpenNext = !_doorChaosOpenNext; StagePhysicsEventsPlugin.ModLogger.LogDebug((object)($"Door Chaos drove {num}/{_doorChaosHinges.Count} fixed hinge(s) " + string.Format("({0} large map door(s)) toward {1}.", num2, doorChaosOpenNext ? "open" : "closed"))); } private void CaptureDoorChaosTargets() { _doorChaosHinges.Clear(); List list = SceneObjects().FindAll(IsEligibleHinge); List list2 = list.FindAll((PhysGrabHinge hinge) => !IsItemHinge(hinge)); List list3 = list.FindAll(IsItemHinge); Shuffle(list2); Shuffle(list3); AddDoorChaosSelection(list2); AddDoorChaosSelection(list3); _doorChaosOpenNext = true; StagePhysicsEventsPlugin.ModLogger.LogDebug((object)($"Door Chaos fixed {_doorChaosHinges.Count} target(s) for this event " + $"from {list.Count} eligible hinge(s), including " + $"{_doorChaosHinges.FindAll(IsLargeMapDoor).Count} large map door(s); " + $"configuredDoors={_filter.Doors}, effectiveDoors={_doorChaosFilter.Doors}.")); } private void AddDoorChaosSelection(List candidates) { if (candidates.Count != 0) { int num = Mathf.Max(1, Mathf.CeilToInt((float)(candidates.Count * _config.DoorChaosAffectedPercent.Value) / 100f)); for (int i = 0; i < Mathf.Min(num, candidates.Count); i++) { _doorChaosHinges.Add(candidates[i]); } } } private bool IsEligibleHinge(PhysGrabHinge hinge) { if (!_doorChaosFilter.Doors || (Object)(object)hinge == (Object)null || (Object)(object)((Component)hinge).GetComponentInParent() != (Object)null || (Object)(object)((Component)hinge).GetComponentInParent() != (Object)null || (Object)(object)((Component)hinge).GetComponentInParent() != (Object)null) { return false; } if (!IsItemHinge(hinge)) { return true; } return _config.DoorChaosHingedItemsEnabled.Value; } private static bool IsItemHinge(PhysGrabHinge hinge) { if (!((Object)(object)((Component)hinge).GetComponentInParent() != (Object)null)) { return (Object)(object)((Component)hinge).GetComponentInParent() != (Object)null; } return true; } private static bool IsLargeMapDoor(PhysGrabHinge hinge) { return !IsItemHinge(hinge); } private static void DriveHinge(PhysGrabHinge hinge, bool open, bool largeDoor, int configuredForce) { //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) object? obj = HingePhysObjectField?.GetValue(hinge); PhysGrabObject val = (PhysGrabObject)(((obj is PhysGrabObject) ? obj : null) ?? ((Component)hinge).GetComponentInParent()); Rigidbody val2 = val?.rb ?? ((Component)hinge).GetComponent() ?? ((Component)hinge).GetComponentInParent(); object? obj2 = HingeJointField?.GetValue(hinge); HingeJoint val3 = (HingeJoint)(((obj2 is HingeJoint) ? obj2 : null) ?? ((Component)hinge).GetComponent()); if (open) { HingeOpenMethod?.Invoke(hinge, null); } else { HingeCloseMethod?.Invoke(hinge, new object[1] { false }); } if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || val2.isKinematic) { return; } if (val != null) { val.OverrideMass(largeDoor ? 1f : 2f, 1.5f); } val2.WakeUp(); Vector3 val4 = ((Component)val3).transform.TransformDirection(((Joint)val3).axis); Vector3 normalized = ((Vector3)(ref val4)).normalized; float num; if (open) { JointLimits limits = val3.limits; num = ((Mathf.Abs(((JointLimits)(ref limits)).max) >= Mathf.Abs(((JointLimits)(ref limits)).min)) ? 1f : (-1f)); if (Mathf.Abs(((JointLimits)(ref limits)).max) < 5f && Mathf.Abs(((JointLimits)(ref limits)).min) < 5f) { num = ((Random.value < 0.5f) ? (-1f) : 1f); } } else { float angle = val3.angle; num = ((Mathf.Abs(angle) > 1f) ? (0f - Mathf.Sign(angle)) : 0f); } float num2 = Mathf.Max(1f, (float)configuredForce) * (largeDoor ? 1f : 0.75f); val2.maxAngularVelocity = Mathf.Max(val2.maxAngularVelocity, num2 * 1.25f); Vector3 val5 = normalized * num * num2; val2.angularVelocity = Vector3.ProjectOnPlane(val2.angularVelocity, normalized) + val5; } private void ApplyValueChangeToNewValuables() { float num = (Has(StageEffect.ValueSurge) ? ((float)_config.ValueSurgeMultiplierPercent.Value / 100f) : ((float)_config.ValueCrashMultiplierPercent.Value / 100f)); int num2 = 0; foreach (ValuableObject item in SceneObjects()) { if ((Object)(object)item == (Object)null) { continue; } object obj = ValuableValueSetField?.GetValue(item); if (!(obj is bool) || (bool)obj) { int key = ValuableKey(item); if (!_savedValuables.ContainsKey(key)) { float num3 = ReadValuableValue(item); float num4 = Mathf.Max(0f, Mathf.Round(num3 * num)); _savedValuables[key] = new SavedValuable(item, num3, num4, num); SetValuableValue(item, num4); num2++; } } } if (num2 > 0) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)((Has(StageEffect.ValueSurge) ? "Value Surge" : "Value Crash") + " updated " + $"{num2} newly discovered valuable(s), including extraction, cart, and truck areas.")); } } private static float ReadValuableValue(ValuableObject valuable) { object obj = ValuableCurrentValueField?.GetValue(valuable); if (obj is float) { return (float)obj; } return 0f; } private static float CalculateRestoredValue(SavedValuable saved) { float num = ReadValuableValue(saved.Valuable); if (Mathf.Approximately(num, saved.AppliedValue)) { return saved.OriginalValue; } return Mathf.Max(0f, Mathf.Round(num / Mathf.Max(0.01f, saved.Multiplier))); } private static void SetValuableValue(ValuableObject valuable, float value) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) try { PhotonView val = ((Component)valuable).GetComponent() ?? ((Component)valuable).GetComponentInParent() ?? ((Component)valuable).GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && (Object)(object)val != (Object)null) { val.RPC("DollarValueSetRPC", (RpcTarget)0, new object[1] { value }); } else { valuable.DollarValueSetRPC(value, default(PhotonMessageInfo)); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Valuable price update failed for " + ((Object)valuable).name + ": " + ex.Message)); } } private static int ValuableKey(ValuableObject valuable) { PhotonView val = ((Component)valuable).GetComponent() ?? ((Component)valuable).GetComponentInParent(); if (!((Object)(object)val != (Object)null) || val.ViewID == 0) { return ((Object)valuable).GetInstanceID(); } return val.ViewID; } private static List SceneObjects() where T : Component { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) List list = new List(); T[] array = Resources.FindObjectsOfTypeAll(); foreach (T val in array) { if ((Object)(object)val != (Object)null) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid() && ((Component)val).gameObject.activeInHierarchy) { list.Add(val); } } } return list; } private static void Shuffle(List list) { for (int i = 0; i < list.Count; i++) { int num = Random.Range(i, list.Count); int index = i; int index2 = num; T value = list[num]; T value2 = list[i]; list[index] = value; list[index2] = value2; } } } internal sealed class VanillaEventNotifier { private readonly MonoBehaviour _coroutineOwner; private readonly StagePhysicsConfig _config; private readonly HashSet _notifiedActors = new HashSet(); private int _stageGeneration; private string _stageMessage = string.Empty; internal VanillaEventNotifier(MonoBehaviour coroutineOwner, StagePhysicsConfig config) { _coroutineOwner = coroutineOwner; _config = config; } internal void BeginStage(int stageGeneration, string stageMessage) { NotificationEnemyReactionGuard.Clear(); _stageGeneration = stageGeneration; _stageMessage = stageMessage; _notifiedActors.Clear(); if (_config.ChatAnnouncementsEnabled.Value && !string.IsNullOrEmpty(_stageMessage) && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient) { _coroutineOwner.StartCoroutine(NotifyAllAfterCapabilityGrace(stageGeneration)); } } internal void PlayerEntered(Player player) { if (_config.ChatAnnouncementsEnabled.Value && !string.IsNullOrEmpty(_stageMessage) && PhotonNetwork.IsMasterClient) { _coroutineOwner.StartCoroutine(NotifyPlayerAfterCapabilityGrace(player, _stageGeneration)); } } internal void EndStage() { NotificationEnemyReactionGuard.Clear(); _stageGeneration++; _notifiedActors.Clear(); _stageMessage = string.Empty; } internal void EventStarted(StageEffect effect) { SendEventMessage(StageEffectSet.FormatForChat(effect, "/")); } internal void Countdown(int seconds) { SendEventMessage(seconds.ToString()); } internal void EventEnded() { SendEventMessage("End"); } private IEnumerator NotifyAllAfterCapabilityGrace(int generation) { yield return (object)new WaitForSeconds(2f); yield return WaitUntilReady(generation); if (CanNotify(generation)) { Player[] playerListOthers = PhotonNetwork.PlayerListOthers; foreach (Player val in playerListOthers) { _notifiedActors.Add(val.ActorNumber); } if (!ForceAllPlayersToSpeak(_stageMessage)) { _notifiedActors.Clear(); } } } private IEnumerator NotifyPlayerAfterCapabilityGrace(Player player, int generation) { yield return (object)new WaitForSeconds(2f); yield return WaitUntilReady(generation); if (CanNotify(generation) && player != null) { NotifyLateJoiner(player); } } private bool CanNotify(int generation) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 if (generation == _stageGeneration && PhotonNetwork.IsMasterClient && (Object)(object)GameDirector.instance != (Object)null) { return (int)GameDirector.instance.currentState == 2; } return false; } private IEnumerator WaitUntilReady(int generation) { float deadline = Time.time + 10f; while (generation == _stageGeneration && PhotonNetwork.IsMasterClient && !CanNotify(generation) && Time.time < deadline) { yield return (object)new WaitForSeconds(0.25f); } } private void NotifyLateJoiner(Player player) { if (_notifiedActors.Add(player.ActorNumber) && !ForceAllPlayersToSpeak(_stageMessage)) { _notifiedActors.Remove(player.ActorNumber); } } private void SendEventMessage(string message) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 if (_config.ChatAnnouncementsEnabled.Value && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && !((Object)(object)GameDirector.instance == (Object)null) && (int)GameDirector.instance.currentState == 2) { ForceAllPlayersToSpeak(message); } } private bool ForceAllPlayersToSpeak(string message) { List list = SemiFunc.PlayerGetList(); if (list == null || list.Count == 0) { return false; } int num = 0; foreach (PlayerAvatar item in list) { if ((Object)(object)item == (Object)null || !((Component)item).gameObject.activeInHierarchy || (Object)(object)item.photonView == (Object)null) { continue; } try { if (!_config.EnemyReactionEnabled.Value) { try { NotificationEnemyReactionGuard.Expect(item, message); } catch (Exception exception) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Enemy-reaction suppression was skipped for player view " + $"{item.photonView.ViewID}: {DescribeException(exception)}")); } } item.ChatMessageSend(message); num++; } catch (Exception exception2) { NotificationEnemyReactionGuard.CancelExpected(item); StagePhysicsEventsPlugin.ModLogger.LogWarning((object)($"Stage event chat failed for player view {item.photonView.ViewID}: " + DescribeException(exception2))); } } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Stage event chat forced through {num} player avatar(s): {message}"); return num > 0; } private static string DescribeException(Exception exception) { Exception ex = exception; while (ex.InnerException != null) { ex = ex.InnerException; } return ex.GetType().Name + ": " + ex.Message; } } internal static class NotificationEnemyReactionGuard { private sealed class ActiveNotification { internal PlayerVoiceChat VoiceChat { get; } internal float AwaitPlaybackUntil { get; } internal float HardExpiresAt { get; } internal bool PlaybackObserved { get; set; } internal float LastPlaybackAt { get; set; } internal float NextMicrophoneInvestigateAt { get; set; } internal int NoTtsCrawlingCounter { get; set; } internal ActiveNotification(PlayerVoiceChat voiceChat, string message) { VoiceChat = voiceChat; AwaitPlaybackUntil = Time.time + 10f; HardExpiresAt = Time.time + Mathf.Clamp(12f + (float)message.Length * 0.25f, 12f, 45f); NextMicrophoneInvestigateAt = Time.time + Mathf.Max(0f, ReadFloat(InvestigateTimerField, voiceChat)); NoTtsCrawlingCounter = ((ReadFloat(ClipLoudnessNoTtsField, voiceChat) > 0.05f) ? ReadInt(ClipLoudnessCrawlingCounterField, voiceChat) : 0); } } private const float PlaybackStartGraceSeconds = 10f; private const float PlaybackSilenceGraceSeconds = 1f; private const float InvestigateCooldownSeconds = 1f; private static readonly FieldInfo? PlayerVoiceChatField = AccessTools.Field(typeof(PlayerAvatar), "voiceChat"); private static readonly FieldInfo? VoiceChatPlayerAvatarField = AccessTools.Field(typeof(PlayerVoiceChat), "playerAvatar"); private static readonly FieldInfo? InvestigateTimerField = AccessTools.Field(typeof(PlayerVoiceChat), "investigateTimer"); private static readonly FieldInfo? ClipLoudnessNoTtsField = AccessTools.Field(typeof(PlayerVoiceChat), "clipLoudnessNoTTS"); private static readonly FieldInfo? ClipLoudnessCrawlingCounterField = AccessTools.Field(typeof(PlayerVoiceChat), "clipLoudnessCrawlingCounter"); private static readonly FieldInfo? PlayerDeathHeadField = AccessTools.Field(typeof(PlayerAvatar), "playerDeathHead"); private static readonly FieldInfo? PlayerDisabledField = AccessTools.Field(typeof(PlayerAvatar), "isDisabled"); private static readonly FieldInfo? PlayerCrawlingField = AccessTools.Field(typeof(PlayerAvatar), "isCrawling"); private static readonly FieldInfo? PlayerCrouchingField = AccessTools.Field(typeof(PlayerAvatar), "isCrouching"); private static readonly FieldInfo? DeathHeadSpectatedField = AccessTools.Field(typeof(PlayerDeathHead), "spectated"); private static readonly FieldInfo? DeathHeadPhysGrabObjectField = AccessTools.Field(typeof(PlayerDeathHead), "physGrabObject"); private static readonly Dictionary ActiveByVoiceChat = new Dictionary(); internal static void Expect(PlayerAvatar player, string message) { PlayerVoiceChat voiceChat = GetVoiceChat(player); if (!((Object)(object)player?.photonView == (Object)null) && !string.IsNullOrEmpty(message) && !((Object)(object)voiceChat == (Object)null)) { ActiveByVoiceChat[((Object)voiceChat).GetInstanceID()] = new ActiveNotification(voiceChat, message); } } internal static void CancelExpected(PlayerAvatar player) { PlayerVoiceChat voiceChat = GetVoiceChat(player); if ((Object)(object)voiceChat != (Object)null) { ActiveByVoiceChat.Remove(((Object)voiceChat).GetInstanceID()); } } internal static bool PrepareVoiceUpdate(PlayerVoiceChat voiceChat) { if (!SuppressionEnabled() || !TryGetActive(voiceChat, out ActiveNotification _)) { return false; } voiceChat.OverrideDisableEnemyInvestigate(0.25f); return true; } internal static void RestoreMicrophoneInvestigation(PlayerVoiceChat voiceChat) { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.IsMultiplayer() || !SemiFunc.IsMasterClient() || (Object)(object)EnemyDirector.instance == (Object)null || !TryGetActive(voiceChat, out ActiveNotification active)) { return; } PlayerAvatar val = ReadReference(VoiceChatPlayerAvatarField, voiceChat); if ((Object)(object)val == (Object)null) { return; } PlayerDeathHead val2 = ReadReference(PlayerDeathHeadField, val); bool flag = (Object)(object)val2 != (Object)null && ReadBool(DeathHeadSpectatedField, val2); if (ReadBool(PlayerDisabledField, val) && !flag) { active.NoTtsCrawlingCounter = 0; return; } bool flag2; if (flag) { active.NoTtsCrawlingCounter = 0; flag2 = ReadFloat(ClipLoudnessNoTtsField, voiceChat) > 0.05f; } else if (ReadBool(PlayerCrawlingField, val)) { if (ReadFloat(ClipLoudnessNoTtsField, voiceChat) > 0.05f) { active.NoTtsCrawlingCounter++; } else { active.NoTtsCrawlingCounter = 0; } flag2 = active.NoTtsCrawlingCounter > 10; } else { active.NoTtsCrawlingCounter = 0; flag2 = ReadFloat(ClipLoudnessNoTtsField, voiceChat) > (ReadBool(PlayerCrouchingField, val) ? 0.05f : 0.025f); } if (flag2 && !(Time.time < active.NextMicrophoneInvestigateAt)) { Vector3 val3 = val.PlayerVisionTarget.VisionTransform.position; PhysGrabObject val4 = (((Object)(object)val2 != (Object)null) ? ReadReference(DeathHeadPhysGrabObjectField, val2) : null); if (flag && (Object)(object)val4 != (Object)null) { val3 = val4.centerPoint; } active.NextMicrophoneInvestigateAt = Time.time + 1f; EnemyDirector.instance.SetInvestigate(val3, 5f, false); } } internal static void Clear() { ActiveByVoiceChat.Clear(); } private static bool TryGetActive(PlayerVoiceChat voiceChat, out ActiveNotification active) { int instanceID = ((Object)voiceChat).GetInstanceID(); if (!ActiveByVoiceChat.TryGetValue(instanceID, out active) || (Object)(object)active.VoiceChat == (Object)null) { ActiveByVoiceChat.Remove(instanceID); active = null; return false; } float time = Time.time; if (time > active.HardExpiresAt) { ActiveByVoiceChat.Remove(instanceID); active = null; return false; } if ((Object)(object)voiceChat.ttsAudioSource != (Object)null && voiceChat.ttsAudioSource.isPlaying) { active.PlaybackObserved = true; active.LastPlaybackAt = time; return true; } if (!active.PlaybackObserved && time <= active.AwaitPlaybackUntil) { return true; } if (active.PlaybackObserved && time - active.LastPlaybackAt <= 1f) { return true; } ActiveByVoiceChat.Remove(instanceID); active = null; return false; } private static bool SuppressionEnabled() { if ((Object)(object)StagePhysicsEventsPlugin.Instance != (Object)null) { return !StagePhysicsEventsPlugin.Instance.Settings.EnemyReactionEnabled.Value; } return false; } private static PlayerVoiceChat? GetVoiceChat(PlayerAvatar? player) { if (!((Object)(object)player == (Object)null)) { return ReadReference(PlayerVoiceChatField, player); } return null; } private static T? ReadReference(FieldInfo? field, object instance) where T : class { return field?.GetValue(instance) as T; } private static float ReadFloat(FieldInfo? field, object instance) { object obj = field?.GetValue(instance); if (obj is float) { return (float)obj; } return 0f; } private static int ReadInt(FieldInfo? field, object instance) { object obj = field?.GetValue(instance); if (obj is int) { return (int)obj; } return 0; } private static bool ReadBool(FieldInfo? field, object instance) { object obj = field?.GetValue(instance); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } } internal static class NotificationEnemyReactionPatches { [HarmonyPrefix] [HarmonyPatch(typeof(PlayerVoiceChat), "Update")] private static void PlayerVoiceChatUpdatePrefix(PlayerVoiceChat __instance, out bool __state) { __state = NotificationEnemyReactionGuard.PrepareVoiceUpdate(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerVoiceChat), "Update")] private static void PlayerVoiceChatUpdatePostfix(PlayerVoiceChat __instance, bool __state) { if (__state) { NotificationEnemyReactionGuard.RestoreMicrophoneInvestigation(__instance); } } } internal sealed class StagePhysicsEventController : MonoBehaviourPunCallbacks { internal const string UiCapabilityKey = "SPE.UI"; private const string VersionKey = "SPE.Version"; private const string StageIdKey = "SPE.StageId"; private const string StateKey = "SPE.State"; private const string EffectKey = "SPE.Effect"; private const string DurationKey = "SPE.DurationSeconds"; private const string IntervalKey = "SPE.IntervalSeconds"; private const string EndTimestampKey = "SPE.EndServerTimestamp"; private const string PreviewEffectKey = "SPE.PreviewEffect"; private const string SlotLimitKey = "SPE.SlotLimit"; private const string PhaseDurationKey = "SPE.PhaseDurationSeconds"; private const string ModeKey = "SPE.Mode"; private const int StateFormatVersion = 11; private const int PersistentEffectDurationSeconds = 300; private const int PersistentRefreshLeadSeconds = 10; private const int IntervalPlanningSeconds = 10; private const int EffectNameDisplaySeconds = 3; private const int StartCountdownSeconds = 5; private const int EndCountdownSeconds = 3; private const float DynamicTargetScanIntervalSeconds = 0.25f; private const float RollPlayerSteeringSpeed = 10f; private const float RollPlayerSteeringAcceleration = 30f; private static readonly FieldInfo? PlayerDeadSetField = AccessTools.Field(typeof(PlayerAvatar), "deadSet"); private static readonly FieldInfo? PlayerSpectatingField = AccessTools.Field(typeof(PlayerAvatar), "spectating"); private static readonly FieldInfo? PlayerHealthValueField = AccessTools.Field(typeof(PlayerHealth), "health"); private static readonly FieldInfo? ExtractionPointsCompletedField = AccessTools.Field(typeof(RoundDirector), "extractionPointsCompleted"); private StagePhysicsConfig _config; private VanillaEffectResolver _resolver; private FeatherOrbEffectAdapter _feather; private StaffAffectBroadcaster _zeroGravity; private AdditionalOrbEffectAdapter _batteryOrb; private AdditionalOrbEffectAdapter _healOrb; private AdditionalOrbEffectAdapter _indestructibleOrb; private StaffAffectBroadcaster _rollStaff; private VoidStaffEffectAdapter _void; private VanillaSpawnEffectResolver _spawnResolver; private RandomStagePointSelector _stagePointSelector; private LevitationEventAdapter _levitation; private GumballHypnosisEventAdapter _gumballHypnosis; private HealingAuraEventAdapter _healingAura; private StarBarrageEventAdapter _starBarrage; private SpiderScareEventAdapter _spiderScare; private TrafficShockEventAdapter _trafficShock; private FlickerEventAdapter _flicker; private EnemyHuntEventAdapter _enemyHunt; private DangerousValuablesEventAdapter _dangerousValuables; private VanillaGrenadeEventAdapter _shockwave; private VanillaGrenadeEventAdapter _stunBlast; private VanillaGrenadeEventAdapter _explosionRain; private EnemyWaveEventAdapter _enemyWave; private MinefieldEventAdapter _minefield; private EnemyPlayerEventAdapter _enemyPlayerEvents; private StageWorldEventAdapter _worldEvents; private ValuableIndestructibleProtection _valuableProtection; private VanillaEventNotifier _notifier; private EventRunState _state; private StageEffect _effect; private float _stateEndsAt; private int _durationSeconds; private int _intervalSeconds; private int _stageToken; private int _stageGeneration; private int _levelGeneratorInstanceId; private bool _stageReady; private bool _wasAuthority; private TargetFilter _activeTargetFilter; private EventMode _mode; private bool _stageEffectsEnabled; private StageEffect _fixedEffect; private StageEffect _plannedEffect; private int _plannedDurationSeconds; private int _plannedNextIntervalSeconds; private bool _intervalPlanPrepared; private bool _intervalNameAnnounced; private int _lastStartCountdownSecond; private int _stageSlotLimit = 3; private int _fixedDurationSeconds; private int _fixedIntervalSeconds; private int _lastExtractionDirectorId; private int _lastExtractionCompletionCount = -1; private EventRunState _remoteState; private StageEffect _remoteEffect; private StageEffect _remotePreviewEffect; private EventMode _remoteMode; private int _remoteSlotLimit = 3; private int _remotePhaseDuration; private int _remoteDuration; private int _remoteInterval; private int _remoteEndTimestamp; private int _remoteStageToken; private Coroutine? _endCountdownCoroutine; private Coroutine? _effectApplicationCoroutine; private int _countdownGeneration; private int _effectApplicationGeneration; private readonly HashSet _activeTargetIds = new HashSet(); private readonly Dictionary _playerAliveStates = new Dictionary(); private float _nextDynamicTargetScanAt; private float _nextVoidSpawnAt; internal void Initialize(StagePhysicsConfig config) { _config = config; _resolver = new VanillaEffectResolver(); _feather = new FeatherOrbEffectAdapter((MonoBehaviour)(object)this, _resolver); _zeroGravity = new StaffAffectBroadcaster((MonoBehaviour)(object)this, _resolver, StaffAffectKind.ZeroGravity); _batteryOrb = new AdditionalOrbEffectAdapter((MonoBehaviour)(object)this, _resolver, config); _healOrb = new AdditionalOrbEffectAdapter((MonoBehaviour)(object)this, _resolver, config); _indestructibleOrb = new AdditionalOrbEffectAdapter((MonoBehaviour)(object)this, _resolver, config); _rollStaff = new StaffAffectBroadcaster((MonoBehaviour)(object)this, _resolver, StaffAffectKind.Roll); _void = new VoidStaffEffectAdapter((MonoBehaviour)(object)this, _resolver); _spawnResolver = new VanillaSpawnEffectResolver(); _stagePointSelector = new RandomStagePointSelector(); _levitation = new LevitationEventAdapter((MonoBehaviour)(object)this, _spawnResolver, _stagePointSelector); _gumballHypnosis = new GumballHypnosisEventAdapter((MonoBehaviour)(object)this, _spawnResolver); _healingAura = new HealingAuraEventAdapter((MonoBehaviour)(object)this, _spawnResolver, _stagePointSelector); _starBarrage = new StarBarrageEventAdapter((MonoBehaviour)(object)this, _spawnResolver, _stagePointSelector); _spiderScare = new SpiderScareEventAdapter((MonoBehaviour)(object)this, _spawnResolver); _trafficShock = new TrafficShockEventAdapter((MonoBehaviour)(object)this, _spawnResolver); _flicker = new FlickerEventAdapter((MonoBehaviour)(object)this, _spawnResolver); _enemyHunt = new EnemyHuntEventAdapter((MonoBehaviour)(object)this, _spawnResolver); _dangerousValuables = new DangerousValuablesEventAdapter(config); _shockwave = new VanillaGrenadeEventAdapter((MonoBehaviour)(object)this, _spawnResolver, _stagePointSelector, StageEffect.Shockwave); _stunBlast = new VanillaGrenadeEventAdapter((MonoBehaviour)(object)this, _spawnResolver, _stagePointSelector, StageEffect.StunBlast); _explosionRain = new VanillaGrenadeEventAdapter((MonoBehaviour)(object)this, _spawnResolver, _stagePointSelector, StageEffect.ExplosionRain); _enemyWave = new EnemyWaveEventAdapter((MonoBehaviour)(object)this, config); _minefield = new MinefieldEventAdapter((MonoBehaviour)(object)this, _spawnResolver, _stagePointSelector, config); _enemyPlayerEvents = new EnemyPlayerEventAdapter(config); _worldEvents = new StageWorldEventAdapter(config); _valuableProtection = new ValuableIndestructibleProtection(); _notifier = new VanillaEventNotifier((MonoBehaviour)(object)this, config); _state = EventRunState.Inactive; } internal void StageReady(LevelGenerator generator) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (!IsPlayableStageContext()) { StageEnding(); return; } int num = (((Object)(object)generator != (Object)null) ? ((Object)generator).GetInstanceID() : 0); if (!_stageReady || _levelGeneratorInstanceId != num) { if (!((Component)this).gameObject.activeSelf) { ((Component)this).gameObject.SetActive(true); } StopLocalEffects(); _valuableProtection.Stop(); _stageReady = true; _levelGeneratorInstanceId = num; _stageGeneration++; _lastExtractionDirectorId = 0; _lastExtractionCompletionCount = -1; Scene activeScene = SceneManager.GetActiveScene(); _stageToken = (((Scene)(ref activeScene)).handle * 397) ^ _stageGeneration ^ PhotonNetwork.ServerTimestamp; PhysGrabObjectRegistry.Clear(); PhysGrabObjectRegistry.SeedFromScene(); SetLocalUiCapability(); ConsumeRoomState(); if (IsAuthority() && _config.Enabled.Value) { _config.LogProbabilityPolicy(); PrepareStagePlan(); _notifier.BeginStage(_stageGeneration, BuildStageStartMessage()); } else { _state = EventRunState.Inactive; } } } internal void StageEnding() { if (!_stageReady && _state == EventRunState.Inactive) { if (((Component)this).gameObject.activeSelf) { ((Component)this).gameObject.SetActive(false); } return; } if (IsAuthority()) { Publish(EventRunState.Inactive, StageEffect.None, 0, 0, 0); } StopLocalEffects(); _valuableProtection.Stop(); _notifier.EndStage(); _stageReady = false; _state = EventRunState.Inactive; _effect = StageEffect.None; _stageEffectsEnabled = false; _fixedEffect = StageEffect.None; _plannedEffect = StageEffect.None; _plannedDurationSeconds = 0; _plannedNextIntervalSeconds = 0; _intervalPlanPrepared = false; _intervalNameAnnounced = false; _lastExtractionDirectorId = 0; _lastExtractionCompletionCount = -1; _remoteState = EventRunState.Inactive; PhysGrabObjectRegistry.Clear(); if (((Component)this).gameObject.activeSelf) { ((Component)this).gameObject.SetActive(false); } } internal bool TryPreventSecondChanceRetake() { if (!IsAuthority() || !_stageReady || _state != EventRunState.Active || !StageEffectSet.Contains(_effect, StageEffect.SecondChance)) { return false; } return _enemyPlayerEvents.TryReviveForLevelFailure(); } internal void Shutdown() { StageEnding(); } internal void RefreshFragilityForPhysics(PhysGrabObjectImpactDetector impactDetector) { if (IsAuthority() && _stageReady && _state == EventRunState.Active && StageEffectSet.Contains(_effect, StageEffect.Fragility) && _activeTargetFilter.Valuables && !((Object)(object)impactDetector == (Object)null)) { PhysGrabObject component = ((Component)impactDetector).GetComponent(); if (!((Object)(object)component == (Object)null) && !((Object)(object)((Component)component).GetComponent() == (Object)null) && !((Object)(object)((Component)component).GetComponent() != (Object)null)) { float num = (float)_config.FragilityMultiplierPercent.Value / 100f; component.OverrideFragility(num); impactDetector.fragilityMultiplier = num; } } } internal bool TryGetHudState(out HudState hudState) { hudState = default(HudState); if (!_stageReady || !IsHudStageActive()) { return false; } if (IsAuthority()) { if (_state == EventRunState.Inactive) { return false; } int remainingSeconds = Mathf.Max(0, Mathf.CeilToInt(_stateEndsAt - Time.time)); int phaseDuration = GetPhaseDuration(_state, _durationSeconds, _intervalSeconds); hudState = new HudState(_state, _effect, _plannedEffect, _mode, _durationSeconds, _intervalSeconds, remainingSeconds, phaseDuration, GetHudSlotLimit(_state, _effect, _plannedEffect, _stageSlotLimit)); return true; } if (_remoteState == EventRunState.Inactive || _remoteStageToken == 0) { return false; } int num = _remoteEndTimestamp - PhotonNetwork.ServerTimestamp; int remainingSeconds2 = Mathf.Max(0, Mathf.CeilToInt((float)num / 1000f)); hudState = new HudState(_remoteState, _remoteEffect, _remotePreviewEffect, _remoteMode, _remoteDuration, _remoteInterval, remainingSeconds2, _remotePhaseDuration, _remoteSlotLimit); return true; } private void Update() { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Invalid comparison between Unknown and I4 DeferredObjectCleanupQueue.ProcessFrame(); if (!_stageReady) { return; } if (!IsPlayableStageContext()) { StageEnding(); return; } bool flag = IsAuthority(); if (_wasAuthority && !flag) { StopLocalEffects(); _valuableProtection.Stop(); _state = EventRunState.Inactive; } _wasAuthority = flag; if (!flag) { return; } if (!_config.Enabled.Value) { if (_state != EventRunState.Inactive) { StopLocalEffects(); _valuableProtection.Stop(); _state = EventRunState.Inactive; Publish(EventRunState.Inactive, StageEffect.None, 0, 0, 0); } return; } RefreshStageSlotLimit(); _valuableProtection.Tick(); if (!_stageEffectsEnabled || (Object)(object)GameDirector.instance == (Object)null || (int)GameDirector.instance.currentState != 2) { return; } if (_state == EventRunState.Inactive) { if (_mode == EventMode.PersistentForStage) { BeginPersistentEvent(); } else { BeginWaiting(NextIntervalSeconds()); } } else if (_state == EventRunState.Waiting) { UpdateWaitingPhase(); if (!(Time.time < _stateEndsAt)) { if (!_intervalPlanPrepared) { PrepareIntervalPlan(); } StageEffect stageEffect = FilterAvailableEffects(_plannedEffect); if (stageEffect == StageEffect.None) { int intervalSeconds = ((_plannedNextIntervalSeconds > 0) ? _plannedNextIntervalSeconds : NextIntervalSeconds()); BeginWaiting(intervalSeconds); } else { BeginEvent(stageEffect, _plannedDurationSeconds, _plannedNextIntervalSeconds); } } } else { if (_state != EventRunState.Active || _effectApplicationCoroutine != null) { return; } _feather.TickFollowOrbs(); _batteryOrb.Tick(); _healOrb.Tick(); _indestructibleOrb.Tick(); RefreshDynamicTargets(); RefreshVoidEffects(); float remainingSeconds = Mathf.Max(0f, _stateEndsAt - Time.time); _levitation.Tick(remainingSeconds); _shockwave.Tick(remainingSeconds); _stunBlast.Tick(remainingSeconds); _explosionRain.Tick(remainingSeconds); _enemyWave.Tick(remainingSeconds); _minefield.Tick(remainingSeconds); _gumballHypnosis.Tick(); _healingAura.Tick(remainingSeconds); _starBarrage.Tick(remainingSeconds); _spiderScare.Tick(remainingSeconds); _trafficShock.Tick(remainingSeconds); _flicker.Tick(remainingSeconds); _enemyHunt.Tick(remainingSeconds); _dangerousValuables.Tick(remainingSeconds); _enemyPlayerEvents.Tick(); _worldEvents.Tick(); if (_mode == EventMode.PersistentForStage) { if (Time.time >= _stateEndsAt - 10f) { RefreshPersistentEvent(); } } else if (_endCountdownCoroutine == null) { if (Time.time >= _stateEndsAt) { EndEventAndBeginWaiting(); } else if (_config.EndCountdownEnabled.Value && Time.time >= _stateEndsAt - 3f) { BeginEndCountdown(); } } } } private void FixedUpdate() { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) if (!_stageReady || _state != EventRunState.Active || !IsAuthority() || !_activeTargetFilter.Players || !StageEffectSet.Contains(_effect, StageEffect.Roll) || (Object)(object)GameDirector.instance == (Object)null) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!IsPlayerAlive(player)) { continue; } PhysGrabObject componentInChildren = ((Component)player).GetComponentInChildren(true); Rigidbody val = (((Object)(object)componentInChildren != (Object)null) ? componentInChildren.rb : null); Transform val2 = (((Object)(object)player.localCamera != (Object)null) ? player.localCamera.GetOverrideTransform() : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null)) { Vector3 val3 = Vector3.ProjectOnPlane(val2.forward, Vector3.up); if (!(((Vector3)(ref val3)).sqrMagnitude < 0.0001f)) { ((Vector3)(ref val3)).Normalize(); Vector3 val4 = Vector3.ProjectOnPlane(val.velocity, Vector3.up); Vector3 val5 = (val3 * 10f - val4) / Mathf.Max(Time.fixedDeltaTime, 0.001f); val5 = Vector3.ClampMagnitude(val5, 30f); val.AddForce(val5, (ForceMode)5); } } } } private bool CanApply(StageEffect effects) { if (effects == StageEffect.None || !StageEffectSet.IsValid(effects)) { return false; } return FilterAvailableEffects(effects) == effects; } private StageEffect FilterAvailableEffects(StageEffect effects) { if (effects == StageEffect.None || !StageEffectSet.IsValid(effects)) { return StageEffect.None; } StageEffect stageEffect = StageEffect.None; foreach (StageEffect individualEffect in StageEffectSet.IndividualEffects) { if (!StageEffectSet.Contains(effects, individualEffect)) { continue; } bool flag; if (individualEffect <= StageEffect.EnemyPurge) { if (individualEffect <= StageEffect.StunBlast) { if (individualEffect <= StageEffect.Roll) { if (individualEffect <= StageEffect.Heal) { StageEffect num = individualEffect - 1; if ((ulong)num <= 3uL) { switch (num) { case StageEffect.None: goto IL_02bd; case StageEffect.Feather: goto IL_02ce; case StageEffect.Feather | StageEffect.ZeroGravity: goto IL_02df; case StageEffect.ZeroGravity: goto IL_0492; } } if (individualEffect != StageEffect.Heal) { goto IL_0492; } flag = _healOrb.EnsureAvailable(individualEffect); } else if (individualEffect != StageEffect.Indestructible) { if (individualEffect != StageEffect.Roll) { goto IL_0492; } flag = _rollStaff.EnsureAvailable(); } else { flag = _indestructibleOrb.EnsureAvailable(individualEffect); } } else if (individualEffect <= StageEffect.Levitation) { if (individualEffect != StageEffect.Void) { if (individualEffect != StageEffect.Levitation) { goto IL_0492; } flag = _levitation.EnsureAvailable(); } else { flag = _void.EnsureAvailable(); } } else if (individualEffect != StageEffect.Shockwave) { if (individualEffect != StageEffect.StunBlast) { goto IL_0492; } flag = _stunBlast.EnsureAvailable(); } else { flag = _shockwave.EnsureAvailable(); } } else { if (individualEffect > StageEffect.Freeze) { if (individualEffect <= StageEffect.EnemyWarp) { if (individualEffect == StageEffect.Stun || individualEffect == StageEffect.EnemyWarp) { goto IL_045e; } } else { if (individualEffect == StageEffect.EnemyHunt) { flag = _enemyHunt.EnsureAvailable(_config.CaptureTargetFilter()); goto IL_0494; } if (individualEffect == StageEffect.EnemyRegen || individualEffect == StageEffect.EnemyPurge) { goto IL_045e; } } goto IL_0492; } if (individualEffect <= StageEffect.EnemyWave) { if (individualEffect != StageEffect.ExplosionRain) { if (individualEffect != StageEffect.EnemyWave) { goto IL_0492; } flag = _enemyWave.EnsureAvailable(); } else { flag = _explosionRain.EnsureAvailable(); } } else { if (individualEffect != StageEffect.Minefield) { if (individualEffect == StageEffect.Freeze) { goto IL_045e; } goto IL_0492; } flag = _minefield.EnsureAvailable(); } } goto IL_0494; } if (individualEffect <= StageEffect.StarBarrage) { if (individualEffect <= StageEffect.Flicker) { if (individualEffect <= StageEffect.SecondChance) { if (individualEffect == StageEffect.DamagePulse || individualEffect == StageEffect.SecondChance) { goto IL_045e; } } else { if (individualEffect == StageEffect.Knockback) { goto IL_045e; } if (individualEffect == StageEffect.Flicker) { flag = _flicker.EnsureAvailable(_config.CaptureTargetFilter()); goto IL_0494; } } } else { if (individualEffect > StageEffect.DoorChaos) { if (individualEffect != StageEffect.HealingAura) { if (individualEffect != StageEffect.StarBarrage) { goto IL_0492; } flag = _starBarrage.EnsureAvailable(); } else { flag = _config.CaptureTargetFilter().Players && _healingAura.EnsureAvailable(); } goto IL_0494; } if (individualEffect == StageEffect.Quake || individualEffect == StageEffect.DoorChaos) { goto IL_0478; } } } else { if (individualEffect > StageEffect.Fragility) { if (individualEffect <= StageEffect.TrafficShock) { if (individualEffect != StageEffect.SpiderScare) { if (individualEffect != StageEffect.TrafficShock) { goto IL_0492; } flag = _trafficShock.EnsureAvailable(_config.CaptureTargetFilter()); } else { flag = _spiderScare.EnsureAvailable(_config.CaptureTargetFilter()); } } else { if (individualEffect != StageEffect.DangerousValuables) { if (individualEffect == StageEffect.EnemySpeedUp || individualEffect == StageEffect.EnemySpeedDown) { goto IL_045e; } goto IL_0492; } flag = _dangerousValuables.EnsureAvailable(); } goto IL_0494; } if (individualEffect <= StageEffect.ValueCrash) { if (individualEffect == StageEffect.ValueSurge || individualEffect == StageEffect.ValueCrash) { goto IL_0478; } } else { if (individualEffect == StageEffect.GumballHypnosis) { flag = _gumballHypnosis.EnsureAvailable(); goto IL_0494; } if (individualEffect == StageEffect.Fragility) { goto IL_0478; } } } goto IL_0492; IL_02df: flag = _batteryOrb.EnsureAvailable(individualEffect); goto IL_0494; IL_02ce: flag = _zeroGravity.EnsureAvailable(); goto IL_0494; IL_02bd: flag = _feather.EnsureAvailable(); goto IL_0494; IL_0494: if (!flag) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Selected {individualEffect}, but its vanilla effect path is unavailable; removing it from the combined event."); } else { stageEffect |= individualEffect; } continue; IL_045e: flag = _enemyPlayerEvents.EnsureAvailable(individualEffect, _config.CaptureTargetFilter()); goto IL_0494; IL_0478: flag = _worldEvents.EnsureAvailable(individualEffect, _config.CaptureTargetFilter()); goto IL_0494; IL_0492: flag = false; goto IL_0494; } return stageEffect; } private void PrepareStagePlan() { _mode = _config.CaptureMode(); _stageSlotLimit = Mathf.Clamp(_config.MaxSimultaneousEffects.Value, 1, 5); _stageEffectsEnabled = _config.RollStageEnabled() && _config.HasSelectableEffect; _fixedEffect = StageEffect.None; _plannedEffect = StageEffect.None; _plannedDurationSeconds = 0; _plannedNextIntervalSeconds = 0; _intervalPlanPrepared = false; _intervalNameAnnounced = false; _lastStartCountdownSecond = 6; _fixedDurationSeconds = 0; _fixedIntervalSeconds = 0; if (_stageEffectsEnabled && UsesStageFixedEffect()) { _fixedEffect = FilterAvailableEffects(_config.RollEffects(_stageSlotLimit)); if (_mode == EventMode.PersistentForStage) { _fixedDurationSeconds = 300; _fixedIntervalSeconds = 0; } else { _fixedDurationSeconds = _config.RollDurationSeconds(); _fixedIntervalSeconds = _config.RollIntervalSeconds(); } _stageEffectsEnabled = _fixedEffect != StageEffect.None && CanApply(_fixedEffect); } if (_stageEffectsEnabled) { ((MonoBehaviour)this).StartCoroutine(ResolveEnabledEffects(_stageGeneration)); if (_mode == EventMode.PersistentForStage) { _state = EventRunState.Inactive; _effect = StageEffect.None; Publish(EventRunState.Inactive, StageEffect.None, 0, 0, 0); } else { BeginWaiting(NextIntervalSeconds()); } StagePhysicsEventsPlugin.ModLogger.LogInfo((object)(UsesStageFixedEffect() ? $"Stage plan enabled: mode={_mode}, effect={_fixedEffect}, duration={_fixedDurationSeconds}s, interval={_fixedIntervalSeconds}s." : $"Stage plan enabled: mode={_mode}.")); } else { StopLocalEffects(); _state = EventRunState.Inactive; _effect = StageEffect.None; Publish(EventRunState.Inactive, StageEffect.None, 0, 0, 0); StagePhysicsEventsPlugin.ModLogger.LogInfo((object)"Stage event activation roll failed or no selectable effect exists; effects and HUD are disabled for this stage."); } } private int NextIntervalSeconds() { if (!UsesFixedTiming()) { return _config.RollIntervalSeconds(); } return _fixedIntervalSeconds; } private void RefreshStageSlotLimit() { int num = Mathf.Clamp(_config.MaxSimultaneousEffects.Value, 1, 5); if (num != _stageSlotLimit) { int stageSlotLimit = _stageSlotLimit; _stageSlotLimit = num; StagePhysicsEventsPlugin.ModLogger.LogInfo((object)("MaxSimultaneousEffects changed during the stage: " + $"{stageSlotLimit} -> {_stageSlotLimit}. The new limit applies to the next roll.")); if (_state != EventRunState.Inactive) { int secondsUntilEnd = Mathf.Max(0, Mathf.CeilToInt(_stateEndsAt - Time.time)); Publish(_state, _effect, _durationSeconds, _intervalSeconds, secondsUntilEnd, GetPhaseDuration(_state, _durationSeconds, _intervalSeconds)); } } } private bool UsesFixedTiming() { if (_mode != EventMode.FixedForStage) { return _mode == EventMode.FixedPerExtraction; } return true; } private bool UsesStageFixedEffect() { if (!UsesFixedTiming()) { return _mode == EventMode.PersistentForStage; } return true; } internal void ExtractionCompleted(RoundDirector? director) { if (_stageReady && IsAuthority() && _config.Enabled.Value && _stageEffectsEnabled && _mode == EventMode.FixedPerExtraction) { int num = (((Object)(object)director != (Object)null) ? ((Object)director).GetInstanceID() : 0); int num2 = ReadExtractionCompletionCount(director); if (num != _lastExtractionDirectorId || num2 != _lastExtractionCompletionCount) { _lastExtractionDirectorId = num; _lastExtractionCompletionCount = num2; StageEffect fixedEffect = FilterAvailableEffects(_config.RollEffects(_stageSlotLimit)); _fixedEffect = fixedEffect; StagePhysicsEventsPlugin.ModLogger.LogInfo((object)($"Completed extraction #{num2}; FixedPerExtraction selected " + $"the effect for the next unplanned interval: {_fixedEffect}.")); } } } private int ReadExtractionCompletionCount(RoundDirector? director) { try { if ((Object)(object)director != (Object)null) { object obj = ExtractionPointsCompletedField?.GetValue(director); if (obj is int) { return (int)obj; } } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Could not read the completed extraction count: " + ex.Message)); } return _lastExtractionCompletionCount + 1; } private IEnumerator ResolveEnabledEffects(int generation) { for (int attempt = 0; attempt < 5; attempt++) { if (!_stageReady) { break; } if (generation != _stageGeneration) { break; } if (!IsAuthority()) { break; } bool flag = true; foreach (StageEffect individualEffect in StageEffectSet.IndividualEffects) { if ((UsesStageFixedEffect() ? StageEffectSet.Contains(_fixedEffect, individualEffect) : _config.IsEffectSelectable(individualEffect)) && !CanApply(individualEffect)) { flag = false; } } if (flag) { break; } yield return (object)new WaitForSeconds(1f); } } private void BeginWaiting(int intervalSeconds) { StopLocalEffects(); _state = EventRunState.Waiting; _effect = StageEffect.None; _plannedEffect = StageEffect.None; _plannedDurationSeconds = 0; _plannedNextIntervalSeconds = 0; _intervalPlanPrepared = false; _intervalNameAnnounced = false; _lastStartCountdownSecond = 6; _durationSeconds = 0; _intervalSeconds = intervalSeconds; _stateEndsAt = Time.time + (float)intervalSeconds; if (intervalSeconds <= 10) { PrepareIntervalPlan(); } else { Publish(_state, _effect, 0, intervalSeconds, intervalSeconds); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)($"Waiting for {intervalSeconds}s before the next stage event. " + $"The event roll begins in the final {10}s.")); } private void UpdateWaitingPhase() { int num = Mathf.Max(0, Mathf.CeilToInt(_stateEndsAt - Time.time)); if (!_intervalPlanPrepared && num <= 10) { PrepareIntervalPlan(); } if (!_intervalPlanPrepared || _plannedEffect == StageEffect.None) { return; } int num2 = (_config.StartCountdownEnabled.Value ? 8 : 3); if (!_intervalNameAnnounced && num <= num2) { _intervalNameAnnounced = true; _notifier.EventStarted(_plannedEffect); } if (_config.StartCountdownEnabled.Value && num <= 5) { int num3 = Mathf.Clamp(num, 0, 5); for (int num4 = _lastStartCountdownSecond - 1; num4 >= num3; num4--) { _notifier.Countdown(num4); } _lastStartCountdownSecond = num3; } } private void PrepareIntervalPlan() { if (!_intervalPlanPrepared) { _intervalPlanPrepared = true; if (_mode == EventMode.PersistentForStage) { _plannedEffect = FilterAvailableEffects(_fixedEffect); _plannedDurationSeconds = 300; _plannedNextIntervalSeconds = 0; } else { _plannedEffect = (UsesFixedTiming() ? FilterAvailableEffects(_fixedEffect) : FilterAvailableEffects(_config.RollEffects(_stageSlotLimit))); _plannedDurationSeconds = (UsesFixedTiming() ? _fixedDurationSeconds : _config.RollDurationSeconds()); _plannedNextIntervalSeconds = NextIntervalSeconds(); } int num = Mathf.Max(0, Mathf.CeilToInt(_stateEndsAt - Time.time)); Publish(_state, _effect, 0, _intervalSeconds, num, _intervalSeconds); StagePhysicsEventsPlugin.ModLogger.LogInfo((object)($"Interval plan selected with {num}s remaining: " + $"effect={_plannedEffect}, duration={_plannedDurationSeconds}s, " + $"next interval={_plannedNextIntervalSeconds}s.")); } } private void BeginEvent(StageEffect effect, int durationSeconds, int nextIntervalSeconds) { _state = EventRunState.Active; _effect = effect; _plannedEffect = effect; _durationSeconds = durationSeconds; _intervalSeconds = nextIntervalSeconds; _activeTargetFilter = _config.CaptureTargetFilter(); _stateEndsAt = Time.time + (float)durationSeconds; Publish(_state, effect, durationSeconds, nextIntervalSeconds, durationSeconds); ScheduleSelectedEffectsApplication(); StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Stage effect started: {effect}, duration={durationSeconds}s, next interval={nextIntervalSeconds}s."); } private void BeginEndCountdown() { int countdownGeneration = ++_countdownGeneration; _endCountdownCoroutine = ((MonoBehaviour)this).StartCoroutine(RunEndCountdown(_stageGeneration, countdownGeneration)); StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Stage effect end countdown: {_effect}, {3}s."); } private IEnumerator RunEndCountdown(int stageGeneration, int countdownGeneration) { for (int seconds = 3; seconds >= 1; seconds--) { if (!EndCountdownIsValid(stageGeneration, countdownGeneration)) { _endCountdownCoroutine = null; yield break; } _notifier.Countdown(seconds); yield return (object)new WaitForSeconds(1f); } if (!EndCountdownIsValid(stageGeneration, countdownGeneration)) { _endCountdownCoroutine = null; yield break; } _endCountdownCoroutine = null; EndEventAndBeginWaiting(); } private bool EndCountdownIsValid(int stageGeneration, int countdownGeneration) { if (_stageReady && _stageGeneration == stageGeneration && _countdownGeneration == countdownGeneration && _state == EventRunState.Active && _mode != EventMode.PersistentForStage && IsAuthority()) { return _config.Enabled.Value; } return false; } private void BeginPersistentEvent() { if (_fixedEffect == StageEffect.None || !CanApply(_fixedEffect)) { _stageEffectsEnabled = false; _state = EventRunState.Inactive; Publish(EventRunState.Inactive, StageEffect.None, 0, 0, 0); } else { BeginWaiting(10); } } private void RefreshPersistentEvent() { _durationSeconds = 300; _stateEndsAt = Time.time + 300f; Publish(_state, _effect, _durationSeconds, 0, _durationSeconds); ScheduleSelectedEffectsApplication(); StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Persistent stage effect refreshed: {_effect}, next refresh in {290}s."); } private void EndEventAndBeginWaiting() { StageEffect effect = _effect; int intervalSeconds = _intervalSeconds; StopLocalEffects(); StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Stage effect ended: {effect}."); _notifier.EventEnded(); BeginWaiting(intervalSeconds); } private string BuildStageStartMessage() { if (!_stageEffectsEnabled) { return string.Empty; } return "Effects"; } private void ScheduleSelectedEffectsApplication() { int generation = ++_effectApplicationGeneration; if (_effectApplicationCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_effectApplicationCoroutine); } _effectApplicationCoroutine = ((MonoBehaviour)this).StartCoroutine(ApplySelectedEffectsStaged(generation)); } private IEnumerator ApplySelectedEffectsStaged(int generation) { yield return null; if (!EffectApplicationIsValid(generation)) { _effectApplicationCoroutine = null; yield break; } List objects = PhysGrabObjectRegistry.Snapshot(_activeTargetFilter); List players = CollectPlayers(); int num = 0; foreach (PhysGrabObject item in objects) { if ((Object)(object)item != (Object)null && ((Object)(object)((Component)item).GetComponent() != (Object)null || (Object)(object)((Component)item).GetComponentInParent() != (Object)null || (Object)(object)((Component)item).GetComponentInParent() != (Object)null)) { num++; } } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Stage effect target snapshot: objects={objects.Count}, enemies={num}, players={(_activeTargetFilter.Players ? players.Count : 0)}."); _activeTargetIds.Clear(); foreach (PhysGrabObject item2 in objects) { if ((Object)(object)item2 != (Object)null) { _activeTargetIds.Add(((Object)item2).GetInstanceID()); } } ScheduleValuableProtection(objects); yield return null; if (!EffectApplicationIsValid(generation)) { _effectApplicationCoroutine = null; yield break; } if (StageEffectSet.Contains(_effect, StageEffect.Feather)) { FeatherOrbEffectAdapter feather = _feather; object players2; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players2 = readOnlyList; } else { players2 = players; } feather.ApplyOnce(objects, (IReadOnlyList)players2); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.ZeroGravity)) { StaffAffectBroadcaster zeroGravity = _zeroGravity; object players3; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players3 = readOnlyList; } else { players3 = players; } zeroGravity.ApplyOnce(objects, (IReadOnlyList)players3, _durationSeconds); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.Battery)) { AdditionalOrbEffectAdapter batteryOrb = _batteryOrb; long effect = 4L; object players4; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players4 = readOnlyList; } else { players4 = players; } batteryOrb.ApplyOnce((StageEffect)effect, objects, (IReadOnlyList)players4); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.Heal)) { AdditionalOrbEffectAdapter healOrb = _healOrb; long effect2 = 8L; object players5; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players5 = readOnlyList; } else { players5 = players; } healOrb.ApplyOnce((StageEffect)effect2, objects, (IReadOnlyList)players5); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.Indestructible)) { AdditionalOrbEffectAdapter indestructibleOrb = _indestructibleOrb; long effect3 = 16L; object players6; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players6 = readOnlyList; } else { players6 = players; } indestructibleOrb.ApplyOnce((StageEffect)effect3, objects, (IReadOnlyList)players6); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.Roll)) { StaffAffectBroadcaster rollStaff = _rollStaff; object players7; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players7 = readOnlyList; } else { players7 = players; } rollStaff.ApplyOnce(objects, (IReadOnlyList)players7, _durationSeconds); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.Void)) { _void.ApplyOnce(objects, players, _config.RollVoidSpawnCount()); _nextVoidSpawnAt = Time.time + (float)_config.VoidSpawnIntervalSeconds.Value; yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.Levitation)) { _levitation.Begin(_config.RollLevitationSpawnCount(), _config.LevitationSpawnIntervalSeconds.Value, _config.LevitationMinimumPlayerDistance.Value, _config.LevitationMaximumActiveInstances.Value); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.Shockwave)) { _shockwave.Begin(_config.RollShockwaveSpawnCount(), _config.ShockwaveSpawnIntervalSeconds.Value, _config.ShockwaveMinimumPlayerDistance.Value, _config.ShockwaveMaximumActiveInstances.Value, _config.ShockwaveLaunchForceMin.Value, _config.ShockwaveLaunchForceMax.Value); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.StunBlast)) { _stunBlast.Begin(_config.RollStunBlastSpawnCount(), _config.StunBlastSpawnIntervalSeconds.Value, _config.StunBlastMinimumPlayerDistance.Value, _config.StunBlastMaximumActiveInstances.Value, _config.StunBlastLaunchForceMin.Value, _config.StunBlastLaunchForceMax.Value); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.ExplosionRain)) { _explosionRain.Begin(_config.RollExplosionRainSpawnCount(), _config.ExplosionRainSpawnIntervalSeconds.Value, _config.ExplosionRainMinimumPlayerDistance.Value, _config.ExplosionRainMaximumActiveInstances.Value, _config.ExplosionRainLaunchForceMin.Value, _config.ExplosionRainLaunchForceMax.Value); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.EnemyWave)) { _enemyWave.Begin(_config.RollEnemyWaveSpawnCount()); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.Minefield)) { _minefield.Begin(_config.RollMinefieldSpawnCount()); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.GumballHypnosis)) { _gumballHypnosis.Begin(_activeTargetFilter); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.HealingAura)) { _healingAura.Begin(_config.RollHealingAuraSpawnCount(), _config.HealingAuraHealthPool.Value, _config.HealingAuraSpawnIntervalSeconds.Value, _config.HealingAuraMinimumPlayerDistance.Value, _config.HealingAuraMaximumActiveInstances.Value); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.StarBarrage)) { _starBarrage.Begin(_config.RollStarBarrageProjectileCount(), _config.StarBarrageSpawnIntervalSeconds.Value, _config.StarBarrageMinimumPlayerDistance.Value, _config.StarBarrageMaximumActiveInstances.Value); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.SpiderScare)) { _spiderScare.Begin(_activeTargetFilter, _config.RollSpiderScarePlayerCount(), _config.SpiderScareSpawnIntervalSeconds.Value); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.TrafficShock)) { _trafficShock.Begin(_activeTargetFilter, _config.RollTrafficShockPlayerCount(), _config.TrafficShockPulseIntervalSeconds.Value); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.Flicker)) { _flicker.Begin(_activeTargetFilter, _config.FlickerIntervalSeconds.Value, _config.FlickerIntensityPercent.Value); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.EnemyHunt)) { _enemyHunt.Begin(_config.EnemyHuntRetargetIntervalSeconds.Value); yield return null; } if (StageEffectSet.Contains(_effect, StageEffect.DangerousValuables)) { _dangerousValuables.Begin(_config.RollDangerousValuableActivationCount()); yield return null; } StageEffect stageEffect = _effect & (StageEffect.Freeze | StageEffect.Stun | StageEffect.EnemyWarp | StageEffect.EnemyRegen | StageEffect.EnemyPurge | StageEffect.DamagePulse | StageEffect.SecondChance | StageEffect.Knockback | StageEffect.EnemySpeedUp | StageEffect.EnemySpeedDown); if (stageEffect != StageEffect.None) { _enemyPlayerEvents.Begin(stageEffect, _activeTargetFilter); yield return null; } StageEffect stageEffect2 = _effect & (StageEffect.Quake | StageEffect.DoorChaos | StageEffect.ValueSurge | StageEffect.ValueCrash | StageEffect.Fragility); if (stageEffect2 != StageEffect.None) { _worldEvents.Begin(stageEffect2, _activeTargetFilter); yield return null; } CapturePlayerAliveStates(); _nextDynamicTargetScanAt = Time.time + 0.25f; _effectApplicationCoroutine = null; } private bool EffectApplicationIsValid(int generation) { if (generation == _effectApplicationGeneration && _stageReady && _state == EventRunState.Active && IsAuthority()) { return _config.Enabled.Value; } return false; } private void RefreshDynamicTargets() { if (Time.time < _nextDynamicTargetScanAt) { return; } _nextDynamicTargetScanAt = Time.time + 0.25f; RefreshRevivedPlayers(); foreach (PhysGrabObject item in PhysGrabObjectRegistry.Snapshot(_activeTargetFilter, discoverEnemies: false)) { if (!((Object)(object)item == (Object)null) && _activeTargetIds.Add(((Object)item).GetInstanceID()) && !((Object)(object)((Component)item).GetComponentInParent() != (Object)null)) { ApplyCurrentEffectsToNewTarget(item); } } RefreshDynamicValuableProtection(); } private void RefreshVoidEffects() { if (StageEffectSet.Contains(_effect, StageEffect.Void) && !(Time.time < _nextVoidSpawnAt)) { List objects = PhysGrabObjectRegistry.Snapshot(_activeTargetFilter); List players = CollectPlayers(); _void.ApplyOnce(objects, players, _config.RollVoidSpawnCount()); _nextVoidSpawnAt = Time.time + (float)_config.VoidSpawnIntervalSeconds.Value; StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Void effects regenerated; next random spawn in {_config.VoidSpawnIntervalSeconds.Value}s."); } } private void RefreshDynamicValuableProtection() { if (!_activeTargetFilter.Valuables || !HasActiveValuableProtection()) { _valuableProtection.Stop(); return; } foreach (PhysGrabObject item in PhysGrabObjectRegistry.Snapshot(new TargetFilter(valuables: true, cosmeticBoxes: false, items: false, doors: false, weapons: false, players: false, enemies: false), discoverEnemies: false)) { _valuableProtection.AddTarget(item); } } private void ApplyCurrentEffectsToNewTarget(PhysGrabObject target) { float num = Mathf.Max(0.1f, _stateEndsAt - Time.time); if (StageEffectSet.Contains(_effect, StageEffect.Feather)) { _feather.AddTarget(target); } if (StageEffectSet.Contains(_effect, StageEffect.ZeroGravity)) { _zeroGravity.ApplyOnce((IReadOnlyList)(object)new PhysGrabObject[1] { target }, Array.Empty(), num); } if (StageEffectSet.Contains(_effect, StageEffect.Battery)) { _batteryOrb.AddTarget(target); } if (StageEffectSet.Contains(_effect, StageEffect.Indestructible)) { _indestructibleOrb.AddTarget(target); } if (StageEffectSet.Contains(_effect, StageEffect.Roll)) { _rollStaff.ApplyOnce((IReadOnlyList)(object)new PhysGrabObject[1] { target }, Array.Empty(), num); } if (_activeTargetFilter.Valuables && (Object)(object)((Component)target).GetComponent() != (Object)null && HasActiveValuableProtection()) { _valuableProtection.AddTarget(target); } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Current stage effects added to new target: {((Object)target).name}, effects={_effect}, remaining={num:0.##}s."); } private void CapturePlayerAliveStates() { _playerAliveStates.Clear(); if ((Object)(object)GameDirector.instance == (Object)null) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player != (Object)null) { _playerAliveStates[PlayerKey(player)] = IsPlayerAlive(player); } } } private void RefreshRevivedPlayers() { if (!_activeTargetFilter.Players || (Object)(object)GameDirector.instance == (Object)null) { return; } bool flag = StageEffectSet.Contains(_effect, StageEffect.Feather) || StageEffectSet.Contains(_effect, StageEffect.ZeroGravity) || StageEffectSet.Contains(_effect, StageEffect.Heal) || StageEffectSet.Contains(_effect, StageEffect.Indestructible) || StageEffectSet.Contains(_effect, StageEffect.Roll); HashSet hashSet = new HashSet(); foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null) { continue; } int num = PlayerKey(player); hashSet.Add(num); bool num2 = IsPlayerAlive(player); bool value; bool flag2 = _playerAliveStates.TryGetValue(num, out value) && value; if (!num2) { _playerAliveStates[num] = false; } else if (!flag2 && TryApplyCurrentEffectsToRevivedPlayer(player)) { _playerAliveStates[num] = true; if (flag) { ManualLogSource modLogger = StagePhysicsEventsPlugin.ModLogger; PhotonView photonView = player.photonView; modLogger.LogInfo((object)$"Current stage effects restored after player revival: view={((photonView != null) ? photonView.ViewID : 0)}, effects={_effect}."); } } } foreach (int item in new List(_playerAliveStates.Keys)) { if (!hashSet.Contains(item)) { _playerAliveStates[item] = false; } } } private bool TryApplyCurrentEffectsToRevivedPlayer(PlayerAvatar player) { bool num = StageEffectSet.Contains(_effect, StageEffect.Indestructible); PhysGrabObject componentInChildren = ((Component)player).GetComponentInChildren(true); if (num && ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren.rb == (Object)null)) { return false; } float durationSeconds = Mathf.Max(0.1f, _stateEndsAt - Time.time); if (StageEffectSet.Contains(_effect, StageEffect.Feather)) { _feather.RefreshPlayer(player); } if (StageEffectSet.Contains(_effect, StageEffect.ZeroGravity)) { _zeroGravity.ApplyOnce(Array.Empty(), (IReadOnlyList)(object)new PlayerAvatar[1] { player }, durationSeconds); } if (StageEffectSet.Contains(_effect, StageEffect.Heal)) { _healOrb.AddPlayer(player); } if (StageEffectSet.Contains(_effect, StageEffect.Indestructible) && (Object)(object)componentInChildren != (Object)null) { _indestructibleOrb.AddTarget(componentInChildren); } if (StageEffectSet.Contains(_effect, StageEffect.Roll)) { _rollStaff.ApplyOnce(Array.Empty(), (IReadOnlyList)(object)new PlayerAvatar[1] { player }, durationSeconds); } return true; } private void ScheduleValuableProtection(IReadOnlyList eventTargets) { if (!_activeTargetFilter.Valuables) { _valuableProtection.Stop(); return; } bool num = HasValuableProtection(StageEffect.Roll, _config.RollProtectValuables.Value); bool flag = HasValuableProtection(StageEffect.Void, _config.VoidProtectValuables.Value); bool flag2 = HasProtectedSpawnedHazardEffect(); bool flag3 = num || flag || flag2; bool flag4 = HasValuableProtection(StageEffect.ZeroGravity, _config.ZeroGravityProtectValuables.Value); if (!flag3 && !flag4) { _valuableProtection.Stop(); return; } float startsAt = (flag3 ? Time.time : (_stateEndsAt - 0.5f)); float num2 = _config.ValuableProtectionReleaseDelaySeconds.Value; float endsAt = _stateEndsAt + num2; _valuableProtection.Schedule(eventTargets, startsAt, endsAt); } private bool HasActiveValuableProtection() { if (!HasValuableProtection(StageEffect.ZeroGravity, _config.ZeroGravityProtectValuables.Value) && !HasValuableProtection(StageEffect.Roll, _config.RollProtectValuables.Value) && !HasValuableProtection(StageEffect.Void, _config.VoidProtectValuables.Value)) { return HasProtectedSpawnedHazardEffect(); } return true; } private bool HasProtectedSpawnedHazardEffect() { if (!HasValuableProtection(StageEffect.Levitation, _config.LevitationProtectValuables.Value) && !HasValuableProtection(StageEffect.Shockwave, _config.ShockwaveProtectValuables.Value) && !HasValuableProtection(StageEffect.StunBlast, _config.StunBlastProtectValuables.Value) && !HasValuableProtection(StageEffect.ExplosionRain, _config.ExplosionRainProtectValuables.Value) && !HasValuableProtection(StageEffect.Minefield, _config.MinefieldProtectValuables.Value) && !HasValuableProtection(StageEffect.StarBarrage, _config.StarBarrageProtectValuables.Value) && !HasValuableProtection(StageEffect.DangerousValuables, _config.DangerousValuablesProtectValuables.Value)) { return HasValuableProtection(StageEffect.Quake, _config.QuakeProtectValuables.Value); } return true; } private bool HasValuableProtection(StageEffect effect, bool enabled) { if (enabled) { return StageEffectSet.Contains(_effect, effect); } return false; } private static List CollectPlayers() { List list = new List(); if ((Object)(object)GameDirector.instance == (Object)null) { return list; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (IsPlayerAlive(player)) { list.Add(player); } } return list; } private static int PlayerKey(PlayerAvatar player) { return ((Object)player).GetInstanceID(); } private static bool IsPlayerAlive(PlayerAvatar? player) { if ((Object)(object)player == (Object)null || !((Component)player).gameObject.activeInHierarchy || (Object)(object)player.playerHealth == (Object)null) { return false; } object obj = PlayerDeadSetField?.GetValue(player); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return false; } obj = PlayerSpectatingField?.GetValue(player); bool flag2 = default(bool); int num2; if (obj is bool) { flag2 = (bool)obj; num2 = 1; } else { num2 = 0; } if (((uint)num2 & (flag2 ? 1u : 0u)) != 0) { return false; } if (PlayerHealthValueField?.GetValue(player.playerHealth) is int num3) { return num3 > 0; } return true; } private void StopLocalEffects() { _countdownGeneration++; _effectApplicationGeneration++; if (_effectApplicationCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_effectApplicationCoroutine); _effectApplicationCoroutine = null; } if (_endCountdownCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_endCountdownCoroutine); _endCountdownCoroutine = null; } _feather?.Stop(); _zeroGravity?.Stop(); _batteryOrb?.Stop(); _healOrb?.Stop(); _indestructibleOrb?.Stop(); _rollStaff?.Stop(); _void?.Stop(); _levitation?.Stop(); _shockwave?.Stop(); _stunBlast?.Stop(); _explosionRain?.Stop(); _enemyWave?.Stop(); _minefield?.Stop(); _gumballHypnosis?.Stop(); _healingAura?.Stop(); _starBarrage?.Stop(); _spiderScare?.Stop(); _trafficShock?.Stop(); _flicker?.Stop(); _enemyHunt?.Stop(); _dangerousValuables?.Stop(); _enemyPlayerEvents?.Stop(); _worldEvents?.Stop(); _activeTargetIds.Clear(); _playerAliveStates.Clear(); _nextDynamicTargetScanAt = 0f; _nextVoidSpawnAt = 0f; } private static bool IsAuthority() { return SemiFunc.IsMasterClientOrSingleplayer(); } private static bool IsPlayableStageContext() { try { return SemiFunc.RunIsLevel() && !SemiFunc.RunIsLobby() && !SemiFunc.RunIsShop() && !SemiFunc.RunIsArena() && !SemiFunc.RunIsTutorial(); } catch { return false; } } private static bool IsHudStageActive() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 if (IsPlayableStageContext() && (Object)(object)LevelGenerator.Instance != (Object)null && LevelGenerator.Instance.Generated && (Object)(object)GameDirector.instance != (Object)null) { return (int)GameDirector.instance.currentState == 2; } return false; } private void Publish(EventRunState state, StageEffect effect, int duration, int interval, int secondsUntilEnd, int phaseDurationOverride = -1) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Expected O, but got Unknown if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && PhotonNetwork.CurrentRoom != null) { int num = PhotonNetwork.ServerTimestamp + secondsUntilEnd * 1000; Hashtable val = new Hashtable { [(object)"SPE.Version"] = 11, [(object)"SPE.StageId"] = _stageToken, [(object)"SPE.State"] = (int)state, [(object)"SPE.Effect"] = (long)effect, [(object)"SPE.DurationSeconds"] = duration, [(object)"SPE.IntervalSeconds"] = interval, [(object)"SPE.EndServerTimestamp"] = num, [(object)"SPE.PreviewEffect"] = (long)_plannedEffect, [(object)"SPE.SlotLimit"] = GetHudSlotLimit(state, effect, _plannedEffect, _stageSlotLimit), [(object)"SPE.PhaseDurationSeconds"] = ((phaseDurationOverride >= 0) ? phaseDurationOverride : GetPhaseDuration(state, duration, interval, secondsUntilEnd)), [(object)"SPE.Mode"] = (int)_mode }; PhotonNetwork.CurrentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); } } private static void SetLocalUiCapability() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown if (PhotonNetwork.InRoom && PhotonNetwork.LocalPlayer != null) { PhotonNetwork.LocalPlayer.SetCustomProperties(new Hashtable { [(object)"SPE.UI"] = 1 }, (Hashtable)null, (WebFlags)null); } } private void ConsumeRoomState() { if (PhotonNetwork.CurrentRoom != null) { ReadRoomState(((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties); } } private void ReadRoomState(Hashtable properties) { if (!TryGetInt(properties, "SPE.Version", out var value) || value != 11 || !TryGetInt(properties, "SPE.StageId", out _remoteStageToken) || !TryGetInt(properties, "SPE.State", out var value2) || !TryGetLong(properties, "SPE.Effect", out var value3) || !TryGetInt(properties, "SPE.DurationSeconds", out _remoteDuration) || !TryGetInt(properties, "SPE.IntervalSeconds", out _remoteInterval) || !TryGetInt(properties, "SPE.EndServerTimestamp", out _remoteEndTimestamp) || !TryGetLong(properties, "SPE.PreviewEffect", out var value4) || !TryGetInt(properties, "SPE.SlotLimit", out _remoteSlotLimit) || !TryGetInt(properties, "SPE.PhaseDurationSeconds", out _remotePhaseDuration) || !TryGetInt(properties, "SPE.Mode", out var value5) || !Enum.IsDefined(typeof(EventRunState), value2) || !Enum.IsDefined(typeof(EventMode), value5) || !StageEffectSet.IsValid((StageEffect)value3) || !StageEffectSet.IsValid((StageEffect)value4)) { _remoteState = EventRunState.Inactive; return; } _remoteState = (EventRunState)value2; _remoteEffect = (StageEffect)value3; _remotePreviewEffect = (StageEffect)value4; _remoteMode = (EventMode)value5; _remoteSlotLimit = Mathf.Clamp(_remoteSlotLimit, 1, 5); } private static int GetPhaseDuration(EventRunState state, int duration, int interval, int explicitDuration = -1) { if (explicitDuration >= 0) { return explicitDuration; } return state switch { EventRunState.Waiting => interval, EventRunState.Active => duration, EventRunState.Countdown => 8, _ => 0, }; } private static int GetHudSlotLimit(EventRunState state, StageEffect activeEffect, StageEffect plannedEffect, int configuredLimit) { StageEffect effects = ((state == EventRunState.Waiting) ? plannedEffect : activeEffect); int num = 0; foreach (StageEffect individualEffect in StageEffectSet.IndividualEffects) { if (StageEffectSet.Contains(effects, individualEffect)) { num++; } } return Mathf.Clamp(Mathf.Max(configuredLimit, num), 1, 5); } private static bool TryGetInt(Hashtable properties, string key, out int value) { value = 0; if (((Dictionary)(object)properties).TryGetValue((object)key, out object value2) && value2 is int num) { return (value = num) == num; } return false; } private static bool TryGetLong(Hashtable properties, string key, out long value) { value = 0L; if (!((Dictionary)(object)properties).TryGetValue((object)key, out object value2)) { return false; } if (value2 is long num) { value = num; return true; } if (value2 is int num2) { value = num2; return true; } return false; } public override void OnJoinedRoom() { SetLocalUiCapability(); ConsumeRoomState(); } public override void OnLeftRoom() { _remoteState = EventRunState.Inactive; StageEnding(); } public override void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged) { ConsumeRoomState(); } public override void OnPlayerEnteredRoom(Player newPlayer) { if (_stageReady && IsAuthority()) { _notifier.PlayerEntered(newPlayer); } } public override void OnMasterClientSwitched(Player newMasterClient) { if (_stageReady && PhotonNetwork.LocalPlayer == newMasterClient && _config.Enabled.Value) { StopLocalEffects(); _valuableProtection.Stop(); _stageToken++; PrepareStagePlan(); _notifier.BeginStage(_stageGeneration, BuildStageStartMessage()); } } } internal enum EventDanger { Neutral, Low, Medium, High, Locked } internal static class EventPresentation { internal static string Name(StageEffect effect) { return StageEffectSet.Format(effect, " + "); } internal static EventDanger Danger(StageEffect effect) { if (effect == StageEffect.None) { return EventDanger.Neutral; } EventDanger eventDanger = EventDanger.Low; foreach (StageEffect individualEffect in StageEffectSet.IndividualEffects) { if (StageEffectSet.Contains(effect, individualEffect)) { EventDanger eventDanger2; switch (individualEffect) { case StageEffect.ZeroGravity: case StageEffect.Levitation: case StageEffect.Shockwave: case StageEffect.StunBlast: case StageEffect.Freeze: case StageEffect.Stun: case StageEffect.Knockback: case StageEffect.Flicker: case StageEffect.DoorChaos: case StageEffect.GumballHypnosis: case StageEffect.SpiderScare: eventDanger2 = EventDanger.Medium; break; case StageEffect.Roll: case StageEffect.Void: case StageEffect.ExplosionRain: case StageEffect.EnemyWave: case StageEffect.Minefield: case StageEffect.EnemyWarp: case StageEffect.EnemyHunt: case StageEffect.EnemyRegen: case StageEffect.DamagePulse: case StageEffect.Quake: case StageEffect.StarBarrage: case StageEffect.ValueCrash: case StageEffect.Fragility: case StageEffect.TrafficShock: case StageEffect.DangerousValuables: eventDanger2 = EventDanger.High; break; case StageEffect.EnemySpeedUp: eventDanger2 = EventDanger.High; break; case StageEffect.EnemySpeedDown: eventDanger2 = EventDanger.Low; break; default: eventDanger2 = EventDanger.Low; break; } EventDanger eventDanger3 = eventDanger2; if (eventDanger3 > eventDanger) { eventDanger = eventDanger3; } } } return eventDanger; } internal static Color Color(EventDanger danger) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) return (Color)(danger switch { EventDanger.Low => FromRgb(112, 166, 80), EventDanger.Medium => FromRgb(207, 158, 53), EventDanger.High => FromRgb(184, 65, 67), EventDanger.Locked => FromRgb(91, 94, 91), _ => FromRgb(176, 179, 170), }); } private static Color FromRgb(byte red, byte green, byte blue) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) return new Color((float)(int)red / 255f, (float)(int)green / 255f, (float)(int)blue / 255f, 1f); } } internal sealed class EventIconCatalog { private const string EventResourcePrefix = "REPOJP.StagePhysicsEvents.Assets.EventIcons.Runtime."; private const string UiResourcePrefix = "REPOJP.StagePhysicsEvents.Assets.UI.Runtime."; private readonly Dictionary _textures = new Dictionary(StringComparer.Ordinal); private Texture2D? _lockTexture; private Texture2D? _stopwatchTexture; private Texture2D? _readyTexture; private Texture2D? _panelTexture; private Texture2D? _roundedTexture; private Sprite? _panelSprite; private Sprite? _roundedSprite; internal Texture2D Lock { get { Texture2D obj = _lockTexture; if (obj == null) { Texture2D? obj2 = Load("REPOJP.StagePhysicsEvents.Assets.UI.Runtime.", "Lock") ?? CreateLock(); Texture2D val = obj2; _lockTexture = obj2; obj = val; } return obj; } } internal Texture2D Stopwatch { get { Texture2D obj = _stopwatchTexture; if (obj == null) { Texture2D? obj2 = Load("REPOJP.StagePhysicsEvents.Assets.UI.Runtime.", "Stopwatch") ?? CreateStopwatch(); Texture2D val = obj2; _stopwatchTexture = obj2; obj = val; } return obj; } } internal Texture2D Ready { get { Texture2D obj = _readyTexture; if (obj == null) { Texture2D? obj2 = Load("REPOJP.StagePhysicsEvents.Assets.UI.Runtime.", "Ready") ?? CreateReady(); Texture2D val = obj2; _readyTexture = obj2; obj = val; } return obj; } } internal Sprite PanelSprite { get { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_panelSprite != (Object)null) { return _panelSprite; } _panelTexture = Load("REPOJP.StagePhysicsEvents.Assets.UI.Runtime.", "Panel") ?? CreateSolidPanelTexture(); float num = Mathf.Min(48f, (float)Mathf.Min(((Texture)_panelTexture).width, ((Texture)_panelTexture).height) * 0.2f); _panelSprite = Sprite.Create(_panelTexture, new Rect(0f, 0f, (float)((Texture)_panelTexture).width, (float)((Texture)_panelTexture).height), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(num, num, num, num)); ((Object)_panelSprite).name = "StageFlux_GeneratedPanel"; ((Object)_panelSprite).hideFlags = (HideFlags)61; return _panelSprite; } } internal Sprite RoundedSprite { get { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_roundedSprite != (Object)null) { return _roundedSprite; } _roundedTexture = CreateRoundedTexture(); _roundedSprite = Sprite.Create(_roundedTexture, new Rect(0f, 0f, (float)((Texture)_roundedTexture).width, (float)((Texture)_roundedTexture).height), new Vector2(0.5f, 0.5f), 32f, 0u, (SpriteMeshType)0, new Vector4(12f, 12f, 12f, 12f)); ((Object)_roundedSprite).name = "StageFlux_Rounded"; ((Object)_roundedSprite).hideFlags = (HideFlags)61; return _roundedSprite; } } internal Texture2D Get(StageEffect effect) { string text = ((effect == StageEffect.None) ? "Waiting" : effect.ToString()); if (_textures.TryGetValue(text, out Texture2D value)) { return value; } value = Load("REPOJP.StagePhysicsEvents.Assets.EventIcons.Runtime.", text) ?? CreateFallback(text); _textures[text] = value; return value; } internal void Dispose() { foreach (Texture2D value in _textures.Values) { Object.Destroy((Object)(object)value); } _textures.Clear(); if ((Object)(object)_lockTexture != (Object)null) { Object.Destroy((Object)(object)_lockTexture); } if ((Object)(object)_stopwatchTexture != (Object)null) { Object.Destroy((Object)(object)_stopwatchTexture); } if ((Object)(object)_readyTexture != (Object)null) { Object.Destroy((Object)(object)_readyTexture); } if ((Object)(object)_panelSprite != (Object)null) { Object.Destroy((Object)(object)_panelSprite); } if ((Object)(object)_panelTexture != (Object)null) { Object.Destroy((Object)(object)_panelTexture); } if ((Object)(object)_roundedSprite != (Object)null) { Object.Destroy((Object)(object)_roundedSprite); } if ((Object)(object)_roundedTexture != (Object)null) { Object.Destroy((Object)(object)_roundedTexture); } _lockTexture = null; _stopwatchTexture = null; _readyTexture = null; _panelSprite = null; _panelTexture = null; _roundedSprite = null; _roundedTexture = null; } private static Texture2D? Load(string prefix, string name) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown try { using Stream stream = typeof(EventIconCatalog).Assembly.GetManifestResourceStream(prefix + name + ".png"); if (stream == null) { return null; } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { break; } } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false) { name = "StageFlux_" + name, wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1, hideFlags = (HideFlags)61 }; return ImageConversion.LoadImage(val, array) ? val : null; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)("Could not load HUD icon '" + name + "': " + ex.Message)); return null; } } private static Texture2D CreateFallback(string name) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) Texture2D val = NewTexture(96, 96, "StageFlux_Fallback_" + name); Color value = default(Color); ((Color)(ref value))..ctor(0f, 0f, 0f, 0f); Color val2 = default(Color); ((Color)(ref val2))..ctor(0.7f, 0.72f, 0.68f, 1f); Color[] array = (Color[])(object)new Color[9216]; Array.Fill(array, value); if (name == "EnemySpeedUp" || name == "EnemySpeedDown") { bool flag = name == "EnemySpeedUp"; for (int i = 22; i < 75; i++) { for (int j = 43; j < 53; j++) { array[i * 96 + j] = val2; } } for (int k = 0; k < 25; k++) { int num = (flag ? (22 + k) : (73 - k)); for (int l = 48 - k; l <= 48 + k; l++) { if (l >= 10 && l < 86) { array[num * 96 + l] = val2; } } } val.SetPixels(array); val.Apply(false, true); return val; } for (int m = 18; m < 78; m++) { for (int n = 42; n < 54; n++) { array[m * 96 + n] = val2; } } for (int num2 = 42; num2 < 54; num2++) { for (int num3 = 18; num3 < 78; num3++) { array[num2 * 96 + num3] = val2; } } val.SetPixels(array); val.Apply(false, true); return val; } private static Texture2D CreateLock() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) Texture2D val = NewTexture(96, 96, "StageFlux_Lock"); Color[] array = (Color[])(object)new Color[9216]; Array.Fill(array, new Color(0f, 0f, 0f, 0f)); Color val2 = EventPresentation.Color(EventDanger.Locked); for (int i = 37; i < 80; i++) { for (int j = 22; j < 74; j++) { array[i * 96 + j] = val2; } } for (int k = 14; k < 50; k++) { for (int l = 28; l < 68; l++) { float num = (float)l - 48f; float num2 = (float)k - 37f; float num3 = Mathf.Sqrt(num * num + num2 * num2); if (num3 >= 15f && num3 <= 21f && k <= 40) { array[k * 96 + l] = val2; } } } for (int m = 50; m < 69; m++) { for (int n = 44; n < 52; n++) { array[m * 96 + n] = Color.black; } } val.SetPixels(array); val.Apply(false, true); return val; } private static Texture2D CreateStopwatch() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) Texture2D val = NewTexture(128, 128, "StageFlux_Stopwatch"); Color[] array = (Color[])(object)new Color[16384]; Array.Fill(array, new Color(0f, 0f, 0f, 0f)); Color val2 = default(Color); ((Color)(ref val2))..ctor(0.72f, 0.74f, 0.7f, 1f); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(64f, 70f); for (int i = 0; i < 128; i++) { for (int j = 0; j < 128; j++) { float num = Vector2.Distance(new Vector2((float)j, (float)i), val3); bool num2 = num >= 47f && num <= 51f; if (num < 47f) { array[i * 128 + j] = new Color(0.025f, 0.028f, 0.027f, 0.94f); } if (num2) { array[i * 128 + j] = val2; } } } for (int k = 5; k < 21; k++) { for (int l = 57; l < 71; l++) { array[k * 128 + l] = val2; } } for (int m = 0; m < 9; m++) { for (int n = 50; n < 78; n++) { array[m * 128 + n] = val2; } } val.SetPixels(array); val.Apply(false, true); return val; } private static Texture2D CreateReady() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) Texture2D val = NewTexture(96, 96, "StageFlux_Ready"); Color[] array = (Color[])(object)new Color[9216]; Array.Fill(array, new Color(0f, 0f, 0f, 0f)); Color val2 = default(Color); ((Color)(ref val2))..ctor(0.9f, 0.88f, 0.76f, 1f); Vector2 val3 = default(Vector2); for (int i = 0; i < 3; i++) { ((Vector2)(ref val3))..ctor(30f + (float)i * 18f, 48f); for (int j = 0; j < 96; j++) { for (int k = 0; k < 96; k++) { if (Vector2.Distance(new Vector2((float)k, (float)j), val3) <= 4f) { array[j * 96 + k] = val2; } } } } val.SetPixels(array); val.Apply(false, true); return val; } private static Texture2D CreateSolidPanelTexture() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) Texture2D obj = NewTexture(32, 32, "StageFlux_PanelFallback"); Color[] array = (Color[])(object)new Color[1024]; Array.Fill(array, new Color(0.025f, 0.027f, 0.026f, 0.96f)); obj.SetPixels(array); obj.Apply(false, true); return obj; } private static Texture2D CreateRoundedTexture() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) Texture2D val = NewTexture(32, 32, "StageFlux_RoundedTexture"); Color[] array = (Color[])(object)new Color[1024]; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(15.5f, 15.5f); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(val2.x - 7f, val2.y - 7f); Vector2 val4 = default(Vector2); for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { ((Vector2)(ref val4))..ctor(Mathf.Max(Mathf.Abs((float)j - val2.x) - val3.x, 0f), Mathf.Max(Mathf.Abs((float)i - val2.y) - val3.y, 0f)); float num = Mathf.Clamp01(7.5f - ((Vector2)(ref val4)).magnitude); array[i * 32 + j] = new Color(1f, 1f, 1f, num); } } val.SetPixels(array); val.Apply(false, true); return val; } private static Texture2D NewTexture(int width, int height, string name) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown return new Texture2D(width, height, (TextureFormat)4, false) { name = name, wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1, hideFlags = (HideFlags)61 }; } } internal sealed class StagePhysicsEventHud : MonoBehaviour { private sealed class SlotView { private StageEffect _targetEffect; private bool _targetLocked; private bool _targetWaiting; private EventDanger _targetDanger; private bool _hasTarget; private bool _spinning; private bool _sliding; private bool _slideSwapped; private float _startedAt; private float _stopAt; private float _settleUntil; private float _slideStartsAt; private float _slideSwapAt; private float _slideEndsAt; private int _lastReelFrame = -1; internal RectTransform Rect { get; } private RectTransform FrameRect { get; } private Image Border { get; } private RectTransform IconRect { get; } private RawImage Icon { get; } private TextMeshProUGUI Label { get; } internal SlotView(RectTransform rect, RectTransform frameRect, Image border, RectTransform iconRect, RawImage icon, TextMeshProUGUI label) { Rect = rect; FrameRect = frameRect; Border = border; IconRect = iconRect; Icon = icon; Label = label; } internal void ApplyLayout(bool vertical) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (vertical) { SetRect(FrameRect, new Vector2(-64f, 0f), new Vector2(56f, 56f)); SetRect(((TMP_Text)Label).rectTransform, new Vector2(34f, 0f), new Vector2(126f, 34f)); ((TMP_Text)Label).alignment = (TextAlignmentOptions)513; ((TMP_Text)Label).fontSizeMin = 8f; ((TMP_Text)Label).fontSizeMax = 15f; } else { SetRect(FrameRect, new Vector2(0f, 10f), new Vector2(64f, 64f)); SetRect(((TMP_Text)Label).rectTransform, new Vector2(0f, -35f), new Vector2(86f, 18f)); ((TMP_Text)Label).alignment = (TextAlignmentOptions)514; ((TMP_Text)Label).fontSizeMin = 7f; ((TMP_Text)Label).fontSizeMax = 12f; } } internal void SetTarget(StageEffect effect, bool locked, bool waiting, EventDanger danger, float startTime, int stopOrder, bool immediate, bool slideToTarget, bool forceSpin, EventIconCatalog icons) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) bool num = forceSpin || !_hasTarget || _targetEffect != effect || _targetLocked != locked || _targetWaiting != waiting; _targetEffect = effect; _targetLocked = locked; _targetWaiting = waiting; _targetDanger = danger; _hasTarget = true; if (!num) { ((Graphic)Border).color = EventPresentation.Color(danger); } else if ((immediate && !forceSpin) || locked) { _spinning = false; _sliding = false; ShowTarget(icons); } else if (slideToTarget) { _spinning = false; _sliding = true; _slideSwapped = false; _slideStartsAt = startTime; _slideSwapAt = _slideStartsAt + 0.075f; _slideEndsAt = _slideSwapAt + 0.075f; } else { _sliding = false; _spinning = true; _startedAt = startTime; _stopAt = startTime + 1.1f + (float)stopOrder * 0.18f; _settleUntil = _stopAt + 0.22f; _lastReelFrame = -1; ((TMP_Text)Label).text = string.Empty; ((Graphic)Border).color = EventPresentation.Color(EventDanger.Neutral); } } internal void Tick(float now, EventIconCatalog icons) { //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) if (_sliding) { if (now < _slideStartsAt) { return; } if (now < _slideSwapAt) { float num = Mathf.InverseLerp(_slideStartsAt, _slideSwapAt, now); float num2 = Mathf.Lerp(0f, -44f, num); IconRect.anchoredPosition = new Vector2(0f, num2); return; } if (!_slideSwapped) { _slideSwapped = true; ShowTarget(icons); IconRect.anchoredPosition = new Vector2(0f, 44f); } if (now < _slideEndsAt) { float num3 = Mathf.InverseLerp(_slideSwapAt, _slideEndsAt, now); float num4 = Mathf.Lerp(44f, 0f, num3); IconRect.anchoredPosition = new Vector2(0f, num4); return; } _sliding = false; IconRect.anchoredPosition = Vector2.zero; ((Transform)IconRect).localScale = Vector3.one; } if (_spinning) { if (!(now >= _stopAt)) { int num5 = Mathf.FloorToInt((now - _startedAt) / 0.075f); if (num5 != _lastReelFrame) { _lastReelFrame = num5; int index = Math.Abs(num5 + ((Transform)Rect).GetSiblingIndex()) % StageEffectSet.IndividualEffects.Count; Icon.texture = (Texture)(object)icons.Get(StageEffectSet.IndividualEffects[index]); ((Graphic)Icon).color = new Color(1f, 1f, 1f, 0.72f); } float num6 = Mathf.Repeat((now - _startedAt) / 0.075f, 1f); float num7 = Mathf.Lerp(22f, -22f, num6); IconRect.anchoredPosition = new Vector2(0f, num7); float num8 = 0.7f + 0.3f * Mathf.Abs(Mathf.Cos(num6 * MathF.PI)); ((Transform)IconRect).localScale = new Vector3(1f, num8, 1f); return; } _spinning = false; ShowTarget(icons); _settleUntil = now + 0.22f; } if (now < _settleUntil) { float num9 = 1f - (_settleUntil - now) / 0.22f; float num10 = Mathf.Sin(num9 * MathF.PI) * 7f * (1f - num9); IconRect.anchoredPosition = new Vector2(0f, num10); ((Transform)IconRect).localScale = Vector3.one * (1f + 0.08f * Mathf.Sin(num9 * MathF.PI)); } else { IconRect.anchoredPosition = Vector2.zero; ((Transform)IconRect).localScale = Vector3.one; } } private void ShowTarget(EventIconCatalog icons) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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) ((Graphic)Border).color = EventPresentation.Color(_targetDanger); IconRect.anchoredPosition = Vector2.zero; ((Transform)IconRect).localScale = Vector3.one; ((Graphic)Icon).color = Color.white; if (_targetLocked) { Icon.texture = (Texture)(object)icons.Lock; ((TMP_Text)Label).text = "LOCKED"; } else if (_targetWaiting) { Icon.texture = (Texture)(object)icons.Get(StageEffect.None); ((TMP_Text)Label).text = "NEXT"; } else if (_targetEffect == StageEffect.None) { Icon.texture = (Texture)(object)icons.Ready; ((TMP_Text)Label).text = "READY"; } else { Icon.texture = (Texture)(object)icons.Get(_targetEffect); ((TMP_Text)Label).text = EventPresentation.Name(_targetEffect).ToUpperInvariant(); } } } private const int PhysicalSlotCount = 5; private const float ReelFrameSeconds = 0.075f; private const float FirstStopSeconds = 1.1f; private const float StopStepSeconds = 0.18f; private const float SettleSeconds = 0.22f; private const float SlideTravel = 44f; private StagePhysicsConfig _config; private StagePhysicsEventController _controller; private readonly EventIconCatalog _icons = new EventIconCatalog(); private readonly List _fontTargets = new List(); private readonly SlotView[] _slots = new SlotView[5]; private GameObject? _root; private GameObject? _classicRoot; private GameObject? _graphicalRoot; private RectTransform? _canvasRect; private RectTransform? _classicRect; private RectTransform? _graphicalRect; private Image? _graphicalPanel; private TextMeshProUGUI? _classicText; private TextMeshProUGUI? _headerText; private TextMeshProUGUI? _limitText; private TextMeshProUGUI? _timerLabel; private RectTransform? _timerRoot; private RectTransform? _timerHand; private string _lastText = string.Empty; private string _lastLayout = string.Empty; private string _lastGraphicalState = string.Empty; private EventRunState _lastGraphicalRunState; private StageEffect _lastGraphicalPreviewEffect; private float _nextFontProbeAt; private bool _graphicalInitialized; internal void Initialize(StagePhysicsConfig config, StagePhysicsEventController controller) { _config = config; _controller = controller; } private void Update() { EnsureCreated(); if ((Object)(object)_root == (Object)null) { return; } HudState hudState = default(HudState); bool flag = _config.HudEnabled.Value && _controller.TryGetHudState(out hudState); if (_root.activeSelf != flag) { _root.SetActive(flag); } if (!flag) { _graphicalInitialized = false; _lastGraphicalRunState = EventRunState.Inactive; _lastGraphicalPreviewEffect = StageEffect.None; return; } bool flag2 = !string.Equals(_config.HudStyle.Value, "Classic", StringComparison.OrdinalIgnoreCase); GameObject? classicRoot = _classicRoot; if (classicRoot != null) { classicRoot.SetActive(!flag2); } GameObject? graphicalRoot = _graphicalRoot; if (graphicalRoot != null) { graphicalRoot.SetActive(flag2); } ApplyBackgroundOpacity(); ApplyLayout(flag2); if (flag2) { UpdateGraphical(hudState); } else { UpdateClassic(hudState); } TryApplyRepoUiFont(); } private void EnsureCreated() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_root != (Object)null) { return; } object obj; if (!((Object)(object)HealthUI.instance != (Object)null)) { HUDCanvas instance = HUDCanvas.instance; obj = ((instance != null) ? ((Component)instance).transform : null); } else { obj = ((Component)HealthUI.instance).transform.parent; } Transform val = (Transform)obj; if (!((Object)(object)val == (Object)null)) { _root = new GameObject("StageFlux_HUD", new Type[1] { typeof(RectTransform) }); ((Object)_root).hideFlags = (HideFlags)61; _root.transform.SetParent(val, false); RectTransform val2 = (_canvasRect = _root.GetComponent()); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.pivot = new Vector2(0.5f, 0.5f); val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; if ((Object)(object)HealthUI.instance != (Object)null && (Object)(object)((Component)HealthUI.instance).transform.parent == (Object)(object)val) { _root.transform.SetSiblingIndex(((Component)HealthUI.instance).transform.GetSiblingIndex() + 1); } CreateClassic(); CreateGraphical(); _lastLayout = string.Empty; _lastText = string.Empty; _lastGraphicalState = string.Empty; _root.SetActive(false); } } private void CreateClassic() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) _classicRoot = new GameObject("Classic", new Type[1] { typeof(RectTransform) }); _classicRoot.transform.SetParent(_root.transform, false); _classicRect = _classicRoot.GetComponent(); _classicRect.sizeDelta = new Vector2(700f, 360f); _classicText = CreateText(_classicRoot.transform, "EventText", 28f); RectTransform rectTransform = ((TMP_Text)_classicText).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; ((TMP_Text)_classicText).alignment = (TextAlignmentOptions)514; } private void CreateGraphical() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Expected O, but got Unknown //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Expected O, but got Unknown //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_0353: Unknown result type (might be due to invalid IL or missing references) //IL_0363: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) _graphicalRoot = new GameObject("Graphical", new Type[2] { typeof(RectTransform), typeof(Image) }); _graphicalRoot.transform.SetParent(_root.transform, false); _graphicalRect = _graphicalRoot.GetComponent(); _graphicalPanel = _graphicalRoot.GetComponent(); _graphicalPanel.sprite = _icons.PanelSprite; _graphicalPanel.type = (Type)1; ((Graphic)_graphicalPanel).color = new Color(1f, 1f, 1f, 0.5f); ((Graphic)_graphicalPanel).raycastTarget = false; _headerText = CreateText(_graphicalRoot.transform, "Header", 20f); ((TMP_Text)_headerText).alignment = (TextAlignmentOptions)514; ((TMP_Text)_headerText).fontStyle = (FontStyles)1; _limitText = CreateText(_graphicalRoot.transform, "Limit", 13f); ((TMP_Text)_limitText).alignment = (TextAlignmentOptions)514; ((TMP_Text)_limitText).fontStyle = (FontStyles)1; ((Graphic)_limitText).color = new Color(0.74f, 0.74f, 0.68f, 1f); GameObject val = new GameObject("SharedStopwatch", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(_graphicalRoot.transform, false); _timerRoot = val.GetComponent(); _timerRoot.sizeDelta = new Vector2(100f, 100f); GameObject val2 = new GameObject("Face", new Type[2] { typeof(RectTransform), typeof(RawImage) }); val2.transform.SetParent(val.transform, false); Stretch(val2.GetComponent(), 0f); RawImage component = val2.GetComponent(); component.texture = (Texture)(object)_icons.Stopwatch; ((Graphic)component).raycastTarget = false; GameObject val3 = new GameObject("Hand", new Type[2] { typeof(RectTransform), typeof(Image) }); val3.transform.SetParent(val.transform, false); _timerHand = val3.GetComponent(); _timerHand.anchorMin = new Vector2(0.5f, 0.5f); _timerHand.anchorMax = new Vector2(0.5f, 0.5f); _timerHand.pivot = new Vector2(0.5f, 0f); _timerHand.anchoredPosition = new Vector2(0f, -3f); _timerHand.sizeDelta = new Vector2(2.6f, 21f); Image component2 = val3.GetComponent(); ((Graphic)component2).color = new Color(0.12f, 0.09f, 0.07f, 1f); ((Graphic)component2).raycastTarget = false; GameObject val4 = new GameObject("Hub", new Type[2] { typeof(RectTransform), typeof(Image) }); val4.transform.SetParent(val.transform, false); SetRect(val4.GetComponent(), new Vector2(0f, -3f), new Vector2(7f, 7f)); Image component3 = val4.GetComponent(); component3.sprite = _icons.RoundedSprite; component3.type = (Type)1; ((Graphic)component3).color = new Color(0.12f, 0.09f, 0.07f, 1f); ((Graphic)component3).raycastTarget = false; _timerLabel = CreateText(_graphicalRoot.transform, "TimerLabel", 13f); ((TMP_Text)_timerLabel).alignment = (TextAlignmentOptions)514; ((TMP_Text)_timerLabel).fontStyle = (FontStyles)1; for (int i = 0; i < 5; i++) { _slots[i] = CreateSlot(i); } } private SlotView CreateSlot(int index) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject($"Slot_{index + 1}", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(_graphicalRoot.transform, false); GameObject val2 = new GameObject("DangerFrame", new Type[2] { typeof(RectTransform), typeof(Image) }); val2.transform.SetParent(val.transform, false); RectTransform component = val2.GetComponent(); Image component2 = val2.GetComponent(); component2.sprite = _icons.RoundedSprite; component2.type = (Type)1; ((Graphic)component2).color = EventPresentation.Color(EventDanger.Neutral); ((Graphic)component2).raycastTarget = false; GameObject val3 = new GameObject("Inner", new Type[3] { typeof(RectTransform), typeof(Image), typeof(RectMask2D) }); val3.transform.SetParent(val2.transform, false); Stretch(val3.GetComponent(), 3f); Image component3 = val3.GetComponent(); component3.sprite = _icons.RoundedSprite; component3.type = (Type)1; ((Graphic)component3).color = new Color(0.025f, 0.029f, 0.028f, 0.96f); ((Graphic)component3).raycastTarget = false; val3.GetComponent().padding = Vector4.zero; GameObject val4 = new GameObject("Icon", new Type[3] { typeof(RectTransform), typeof(RawImage), typeof(AspectRatioFitter) }); val4.transform.SetParent(val3.transform, false); RectTransform component4 = val4.GetComponent(); component4.anchorMin = new Vector2(0.08f, 0.08f); component4.anchorMax = new Vector2(0.92f, 0.92f); component4.offsetMin = Vector2.zero; component4.offsetMax = Vector2.zero; AspectRatioFitter component5 = val4.GetComponent(); component5.aspectMode = (AspectMode)3; component5.aspectRatio = 1f; RawImage component6 = val4.GetComponent(); ((Graphic)component6).raycastTarget = false; ((Graphic)component6).color = Color.white; TextMeshProUGUI val5 = CreateText(val.transform, "Label", 15f); ((TMP_Text)val5).alignment = (TextAlignmentOptions)514; ((TMP_Text)val5).fontStyle = (FontStyles)1; ((TMP_Text)val5).enableAutoSizing = true; ((TMP_Text)val5).fontSizeMin = 8f; ((TMP_Text)val5).fontSizeMax = 15f; return new SlotView(val.GetComponent(), component, component2, component4, component6, val5); } private void UpdateClassic(HudState state) { if (!((Object)(object)_classicText == (Object)null)) { string text = BuildClassicText(state); if (_lastText != text) { _lastText = text; ((TMP_Text)_classicText).text = text; } } } private void UpdateGraphical(HudState state) { bool flag = !string.Equals(_config.HudLayoutDirection.Value, "Horizontal", StringComparison.OrdinalIgnoreCase); int num = Mathf.Clamp(state.SlotLimit, 1, 5); StageEffect displayed = ((state.State == EventRunState.Waiting) ? state.PreviewEffect : state.Effect); string text = $"{state.State}:{(long)state.Effect}:{(long)state.PreviewEffect}:{num}:{flag}"; if (_lastGraphicalState != text) { bool immediate = !_graphicalInitialized; bool revealPlannedEvent = state.State == EventRunState.Waiting && state.PreviewEffect != StageEffect.None && (_lastGraphicalRunState != EventRunState.Waiting || _lastGraphicalPreviewEffect != state.PreviewEffect); _lastGraphicalState = text; _graphicalInitialized = true; ApplySlotTargets(state, displayed, num, immediate, revealPlannedEvent); } _lastGraphicalRunState = state.State; _lastGraphicalPreviewEffect = state.PreviewEffect; if ((Object)(object)_headerText != (Object)null) { TextMeshProUGUI? headerText = _headerText; string text2; if (flag) { text2 = "STAGE EVENTS"; } else { string text3 = ((state.State != EventRunState.Waiting) ? "STAGE EVENTS" : "WAITING"); text2 = text3; } ((TMP_Text)headerText).text = text2; } if ((Object)(object)_limitText != (Object)null) { ((TMP_Text)_limitText).text = $"LIMIT {num} / {5}"; } if ((Object)(object)_timerLabel != (Object)null) { ((TMP_Text)_timerLabel).text = ((state.State == EventRunState.Waiting) ? "INTERVAL" : ((state.Mode == EventMode.PersistentForStage) ? "PERSISTENT" : "TIME")); } float unscaledTime = Time.unscaledTime; for (int i = 0; i < 5; i++) { _slots[i].Tick(unscaledTime, _icons); } UpdateStopwatch(state); } private void ApplySlotTargets(HudState state, StageEffect displayed, int slotLimit, bool immediate, bool revealPlannedEvent) { List list = new List(5); foreach (StageEffect individualEffect in StageEffectSet.IndividualEffects) { if (StageEffectSet.Contains(displayed, individualEffect)) { list.Add(individualEffect); } } EventDanger eventDanger = EventPresentation.Danger(state.PreviewEffect); float unscaledTime = Time.unscaledTime; bool flag = state.State == EventRunState.Waiting && displayed != StageEffect.None; for (int i = 0; i < 5; i++) { if (i >= slotLimit) { _slots[i].SetTarget(StageEffect.None, locked: true, waiting: false, EventDanger.Locked, unscaledTime, i, immediate, slideToTarget: false, forceSpin: false, _icons); continue; } if (state.State == EventRunState.Waiting && !flag) { _slots[i].SetTarget(StageEffect.None, locked: false, i == 0, (i == 0) ? eventDanger : EventDanger.Neutral, unscaledTime, i, immediate, slideToTarget: true, forceSpin: false, _icons); continue; } StageEffect effect = ((i < list.Count) ? list[i] : StageEffect.None); _slots[i].SetTarget(effect, locked: false, waiting: false, EventPresentation.Danger(effect), unscaledTime, i, immediate, slideToTarget: false, flag && revealPlannedEvent, _icons); } } private void UpdateStopwatch(HudState state) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_timerHand == (Object)null)) { int num = Mathf.Max(1, state.PhaseDurationSeconds); float num2 = Mathf.Clamp(state.RemainingSeconds, 0, num); float num3 = 1f - num2 / (float)num; ((Transform)_timerHand).localRotation = Quaternion.Euler(0f, 0f, -360f * num3); } } private void ApplyBackgroundOpacity() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_graphicalPanel == (Object)null)) { float num = Mathf.Clamp01((float)_config.HudBackgroundOpacityPercent.Value / 100f); Color color = ((Graphic)_graphicalPanel).color; if (!Mathf.Approximately(color.a, num)) { color.a = num; ((Graphic)_graphicalPanel).color = color; } } } private void ApplyLayout(bool graphical) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) int num; Rect rect; if (!((Object)(object)_canvasRect != (Object)null)) { num = Screen.width; } else { rect = _canvasRect.rect; num = Mathf.RoundToInt(((Rect)(ref rect)).width); } int num2 = num; int num3; if (!((Object)(object)_canvasRect != (Object)null)) { num3 = Screen.height; } else { rect = _canvasRect.rect; num3 = Mathf.RoundToInt(((Rect)(ref rect)).height); } int num4 = num3; string text = $"{graphical}:{_config.HudLayoutDirection.Value}:{_config.HudAnchor.Value}:" + $"{_config.HudAlignment.Value}:{_config.HudOffsetX.Value}:" + $"{_config.HudOffsetY.Value}:{_config.HudScalePercent.Value}:" + $"{num2}x{num4}:{Screen.width}x{Screen.height}"; if (!(_lastLayout == text)) { _lastLayout = text; Vector2 anchor = ResolveAnchor(_config.HudAnchor.Value); float userScale = (float)_config.HudScalePercent.Value / 100f; float resolutionScale = ResolveResolutionScale(); bool vertical = !string.Equals(_config.HudLayoutDirection.Value, "Horizontal", StringComparison.OrdinalIgnoreCase); if ((Object)(object)_classicRect != (Object)null) { float scale = CalculateResponsiveScale(_classicRect.sizeDelta, userScale, resolutionScale); PlaceAtAnchor(_classicRect, anchor, scale, resolutionScale); } if ((Object)(object)_classicText != (Object)null) { TextMeshProUGUI classicText = _classicText; string value = _config.HudAlignment.Value; TextAlignmentOptions alignment = ((value == "Left") ? ((TextAlignmentOptions)513) : ((!(value == "Right")) ? ((TextAlignmentOptions)514) : ((TextAlignmentOptions)516))); ((TMP_Text)classicText).alignment = alignment; } if ((Object)(object)_graphicalRect != (Object)null) { LayoutGraphical(vertical); float scale2 = CalculateResponsiveScale(_graphicalRect.sizeDelta, userScale, resolutionScale); PlaceAtAnchor(_graphicalRect, anchor, scale2, resolutionScale); } _lastGraphicalState = string.Empty; } } private void LayoutGraphical(bool vertical) { //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_graphicalRect == (Object)null || (Object)(object)_headerText == (Object)null || (Object)(object)_limitText == (Object)null || (Object)(object)_timerLabel == (Object)null || (Object)(object)_timerRoot == (Object)null) { return; } RectTransform rectTransform = ((TMP_Text)_headerText).rectTransform; RectTransform rectTransform2 = ((TMP_Text)_limitText).rectTransform; RectTransform rectTransform3 = ((TMP_Text)_timerLabel).rectTransform; if (vertical) { _graphicalRect.sizeDelta = new Vector2(220f, 430f); SetRect(rectTransform, new Vector2(-36f, 188f), new Vector2(124f, 25f)); SetRect(rectTransform2, new Vector2(-36f, 166f), new Vector2(124f, 18f)); SetRect(_timerRoot, new Vector2(70f, 181f), new Vector2(58f, 58f)); SetRect(rectTransform3, new Vector2(70f, 145f), new Vector2(92f, 17f)); for (int i = 0; i < 5; i++) { SetRect(_slots[i].Rect, new Vector2(0f, 111f - (float)i * 62f), new Vector2(196f, 58f)); _slots[i].ApplyLayout(vertical: true); } } else { _graphicalRect.sizeDelta = new Vector2(640f, 160f); SetRect(rectTransform, new Vector2(-210f, 58f), new Vector2(185f, 22f)); ((TMP_Text)_headerText).alignment = (TextAlignmentOptions)513; SetRect(rectTransform2, new Vector2(207f, 58f), new Vector2(180f, 18f)); ((TMP_Text)_limitText).alignment = (TextAlignmentOptions)516; for (int j = 0; j < 5; j++) { SetRect(_slots[j].Rect, new Vector2(-220f + (float)j * 94f, 0f), new Vector2(88f, 98f)); _slots[j].ApplyLayout(vertical: false); } SetRect(_timerRoot, new Vector2(252f, 2f), new Vector2(66f, 66f)); SetRect(rectTransform3, new Vector2(252f, -41f), new Vector2(86f, 18f)); } if (vertical) { ((TMP_Text)_headerText).alignment = (TextAlignmentOptions)514; ((TMP_Text)_limitText).alignment = (TextAlignmentOptions)514; } } private float ResolveResolutionScale() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) float num; if ((Object)(object)_canvasRect != (Object)null) { Rect rect = _canvasRect.rect; if (((Rect)(ref rect)).height > 1f) { rect = _canvasRect.rect; num = ((Rect)(ref rect)).height; goto IL_0043; } } num = Screen.height; goto IL_0043; IL_0043: float num2 = num; return Mathf.Max(0.1f, num2 / 540f); } private float CalculateResponsiveScale(Vector2 layoutSize, float userScale, float resolutionScale) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(0.1f, resolutionScale * userScale); if (!((Object)(object)_canvasRect == (Object)null)) { Rect rect = _canvasRect.rect; if (!(((Rect)(ref rect)).width <= 1f)) { rect = _canvasRect.rect; if (!(((Rect)(ref rect)).height <= 1f) && !(layoutSize.x <= 1f) && !(layoutSize.y <= 1f)) { float num2 = 8f * resolutionScale; rect = _canvasRect.rect; float num3 = Mathf.Max(1f, ((Rect)(ref rect)).width - num2 * 2f); rect = _canvasRect.rect; float num4 = Mathf.Max(1f, ((Rect)(ref rect)).height - num2 * 2f); float num5 = Mathf.Min(num3 / layoutSize.x, num4 / layoutSize.y); return Mathf.Max(0.1f, Mathf.Min(num, num5)); } } } return num; } private void PlaceAtAnchor(RectTransform rect, Vector2 anchor, float scale, float resolutionScale) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = anchor; rect.anchorMax = anchor; rect.pivot = anchor; rect.anchoredPosition = new Vector2((float)_config.HudOffsetX.Value * resolutionScale, (float)_config.HudOffsetY.Value * resolutionScale); ((Transform)rect).localScale = Vector3.one * scale; } private TextMeshProUGUI CreateText(Transform parent, string name, float size) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val.transform.SetParent(parent, false); TextMeshProUGUI component = val.GetComponent(); ((TMP_Text)component).fontSize = size; ((Graphic)component).color = new Color(0.9f, 0.91f, 0.87f, 1f); ((TMP_Text)component).outlineColor = Color32.op_Implicit(new Color(0f, 0f, 0f, 0.95f)); ((TMP_Text)component).outlineWidth = 0.15f; ((Graphic)component).raycastTarget = false; ((TMP_Text)component).enableWordWrapping = false; _fontTargets.Add((TMP_Text)(object)component); return component; } private void TryApplyRepoUiFont() { if (Time.unscaledTime < _nextFontProbeAt) { return; } _nextFontProbeAt = Time.unscaledTime + 2f; TMP_Text val = (TMP_Text)(object)(((Object)(object)HealthUI.instance != (Object)null) ? ((Component)HealthUI.instance).GetComponent() : null); if (val == null) { val = (TMP_Text)(object)ChatUI.instance?.chatText; } if ((Object)(object)val == (Object)null && (Object)(object)HUD.instance != (Object)null) { TMP_Text[] componentsInChildren = ((Component)HUD.instance).GetComponentsInChildren(true); foreach (TMP_Text val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null && !_fontTargets.Contains(val2) && (Object)(object)val2.font != (Object)null) { val = val2; break; } } } if ((Object)(object)((val != null) ? val.font : null) == (Object)null) { return; } foreach (TMP_Text fontTarget in _fontTargets) { if ((Object)(object)fontTarget != (Object)null && (Object)(object)fontTarget.font != (Object)(object)val.font) { fontTarget.font = val.font; } } } private static string BuildClassicText(HudState state) { if (state.State == EventRunState.Waiting) { return $"Waiting\nInterval: {state.IntervalSeconds}s"; } if (state.State == EventRunState.Countdown) { return StageEffectSet.Format(state.Effect, "\n"); } if (state.IntervalSeconds == 0) { return StageEffectSet.Format(state.Effect, "\n") + "\nMode: Persistent"; } return string.Format("{0}\nDuration: {1}s", StageEffectSet.Format(state.Effect, "\n"), state.DurationSeconds); } private static Vector2 ResolveAnchor(string anchor) { //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) return (Vector2)(anchor switch { "TopLeft" => new Vector2(0f, 1f), "TopCenter" => new Vector2(0.5f, 1f), "TopRight" => new Vector2(1f, 1f), "MiddleLeft" => new Vector2(0f, 0.5f), "MiddleCenter" => new Vector2(0.5f, 0.5f), "MiddleRight" => new Vector2(1f, 0.5f), "BottomLeft" => new Vector2(0f, 0f), "BottomCenter" => new Vector2(0.5f, 0f), "BottomRight" => new Vector2(1f, 0f), _ => new Vector2(1f, 0f), }); } private static void Stretch(RectTransform rect, float inset) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.offsetMin = new Vector2(inset, inset); rect.offsetMax = new Vector2(0f - inset, 0f - inset); } private static void SetRect(RectTransform rect, Vector2 position, Vector2 size) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = new Vector2(0.5f, 0.5f); rect.anchorMax = new Vector2(0.5f, 0.5f); rect.pivot = new Vector2(0.5f, 0.5f); rect.anchoredPosition = position; rect.sizeDelta = size; } private void OnDestroy() { _icons.Dispose(); if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } } } internal static class LifecyclePatches { [HarmonyPostfix] [HarmonyPatch(typeof(LevelGenerator), "GenerateDone")] private static void LevelGeneratorGenerateDonePostfix(LevelGenerator __instance) { StagePhysicsEventsPlugin.Instance?.Controller?.StageReady(__instance); } [HarmonyPrefix] [HarmonyPatch(typeof(RunManager), "ChangeLevel")] private static bool RunManagerChangeLevelPrefix(RunManager __instance, bool _levelFailed) { StagePhysicsEventController stagePhysicsEventController = StagePhysicsEventsPlugin.Instance?.Controller; if (_levelFailed && (Object)(object)stagePhysicsEventController != (Object)null && stagePhysicsEventController.TryPreventSecondChanceRetake()) { __instance.AllPlayersDeadSet(false); StagePhysicsEventsPlugin.ModLogger.LogInfo((object)"Second Chance prevented a failed-stage retake and revived the player at the death location."); return false; } stagePhysicsEventController?.StageEnding(); return true; } [HarmonyPostfix] [HarmonyPatch(typeof(RoundDirector), "ExtractionCompleted")] private static void RoundDirectorExtractionCompletedPostfix(RoundDirector __instance) { StagePhysicsEventsPlugin.Instance?.Controller?.ExtractionCompleted(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(PhysGrabObject), "Start")] private static void PhysGrabObjectStartPostfix(PhysGrabObject __instance) { PhysGrabObjectRegistry.Register(__instance); } [HarmonyPrefix] [HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "FixedUpdate")] private static void PhysGrabObjectImpactDetectorFixedUpdatePrefix(PhysGrabObjectImpactDetector __instance) { StagePhysicsEventsPlugin.Instance?.Controller?.RefreshFragilityForPhysics(__instance); } } }