using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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.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.1.0.0")] [assembly: AssemblyInformationalVersion("4.1.0")] [assembly: AssemblyProduct("StagePhysicsEvents")] [assembly: AssemblyTitle("StagePhysicsEvents")] [assembly: AssemblyVersion("4.1.0.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 Physics Events", "4.1.0")] public sealed class StagePhysicsEventsPlugin : BaseUnityPlugin { public const string PluginGuid = "REPOJP.StagePhysicsEvents"; public const string PluginName = "Stage Physics Events"; public const string PluginVersion = "4.1.0"; 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)); SceneManager.activeSceneChanged += ActiveSceneChanged; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Stage Physics Events 4.1.0 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 { private const int CurrentConfigRevision = 2; 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 StageEffectChancePercent { get; } internal ConfigEntry MaxSimultaneousEffects { 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 ZeroGravityIndestructibleReleaseDelaySeconds { 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 RollEnabled { get; } internal ConfigEntry RollChance { get; } internal ConfigEntry RollIndestructibleReleaseDelaySeconds { get; } internal ConfigEntry VoidEnabled { get; } internal ConfigEntry VoidChance { get; } internal ConfigEntry VoidIndestructibleReleaseDelaySeconds { get; } internal ConfigEntry VoidSpawnCountMin { get; } internal ConfigEntry VoidSpawnCountMax { get; } internal ConfigEntry FloatValuables { get; } internal ConfigEntry FloatItems { get; } internal ConfigEntry FloatDoors { get; } internal ConfigEntry FloatWeapons { get; } internal ConfigEntry FloatPlayers { get; } internal ConfigEntry FloatEnemies { get; } internal ConfigEntry UiEnabled { get; } internal ConfigEntry StageStartChatEnabled { get; } internal ConfigEntry CountdownEnabled { get; } internal ConfigEntry UiAnchor { get; } internal ConfigEntry UiAlignment { get; } internal ConfigEntry UiOffsetX { get; } internal ConfigEntry UiOffsetY { get; } internal ConfigEntry UiScalePercent { 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 StagePhysicsConfig(ConfigFile config, ManualLogSource logger) { //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Expected O, but got Unknown //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Expected O, but got Unknown //IL_077a: Unknown result type (might be due to invalid IL or missing references) //IL_0784: Expected O, but got Unknown //IL_07c7: Unknown result type (might be due to invalid IL or missing references) //IL_07d1: Expected O, but got Unknown //IL_084d: Unknown result type (might be due to invalid IL or missing references) //IL_0857: Expected O, but got Unknown //IL_085f: Unknown result type (might be due to invalid IL or missing references) //IL_0869: Expected O, but got Unknown //IL_0875: Unknown result type (might be due to invalid IL or missing references) //IL_087f: Expected O, but got Unknown //IL_088b: Unknown result type (might be due to invalid IL or missing references) //IL_0895: Expected O, but got Unknown //IL_08a1: Unknown result type (might be due to invalid IL or missing references) //IL_08ab: Expected O, but got Unknown //IL_08b7: Unknown result type (might be due to invalid IL or missing references) //IL_08c1: Expected O, but got Unknown //IL_08cd: Unknown result type (might be due to invalid IL or missing references) //IL_08d7: Expected O, but got Unknown //IL_08e3: Unknown result type (might be due to invalid IL or missing references) //IL_08ed: Expected O, but got Unknown //IL_08f9: Unknown result type (might be due to invalid IL or missing references) //IL_0903: Expected O, but got Unknown //IL_090f: Unknown result type (might be due to invalid IL or missing references) //IL_0919: 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", 15, 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", 10, 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."); 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())); StageEffectChancePercent = BindInt(config, "General", "StageEffectChancePercent", 20, 0, 100, "Chance that stage effects are enabled for a stage. A failed roll disables both effects and the HUD for that stage."); MaxSimultaneousEffects = BindInt(config, "General", "MaxSimultaneousEffects", 3, 1, StageEffectSet.IndividualEffects.Count, "Maximum number of effects that can be selected for one event. Successful effects above this limit are reduced randomly."); 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."); 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."); 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", 10, 0, 100, "Independent chance for Zero Gravity in each event."); ZeroGravityIndestructibleReleaseDelaySeconds = BindInt(config, "Zero Gravity", "IndestructibleReleaseDelaySeconds", 2, 1, 5, "Seconds to keep valuables Indestructible after Zero Gravity ends."); BatteryEnabled = BindBool(config, "Battery", "Enabled", value: true, "Includes the Battery Orb effect in each event's independent rolls."); BatteryChance = BindInt(config, "Battery", "ChancePercent", 5, 0, 100, "Independent chance for Battery in each event."); BatteryChargeAmount = BindInt(config, "Battery", "ChargeAmount", 5, 1, 100, "Battery percentage points restored to each targeted battery per charge tick."); BatteryChargeIntervalSeconds = BindInt(config, "Battery", "ChargeIntervalSeconds", 4, 1, 300, "Seconds between Battery charge ticks."); if (num < 2) { if (BatteryChargeAmount.Value == 10) { BatteryChargeAmount.Value = 5; _logger.LogInfo((object)"Migrated the previous default Battery charge amount from 10 to 5."); } if (BatteryChargeIntervalSeconds.Value == 2) { BatteryChargeIntervalSeconds.Value = 4; _logger.LogInfo((object)"Migrated the previous default Battery charge interval from 2 seconds to 4 seconds."); } } if (num < 2) { WriteConfigRevision(config, definition); } 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."); 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."); RollIndestructibleReleaseDelaySeconds = BindInt(config, "Roll", "IndestructibleReleaseDelaySeconds", 2, 1, 5, "Seconds to keep valuables Indestructible after Roll ends."); 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."); VoidIndestructibleReleaseDelaySeconds = BindInt(config, "Void", "IndestructibleReleaseDelaySeconds", 2, 1, 5, "Seconds to keep valuables Indestructible after Void ends."); 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."); FloatValuables = BindBool(config, "What floats", "Valuables", value: true, "Targets valuables."); FloatItems = BindBool(config, "What floats", "Items", value: true, "Targets general items, carts, and movable props."); FloatDoors = BindBool(config, "What floats", "Doors", value: false, "Targets doors, lids, and other hinged movable objects."); FloatWeapons = BindBool(config, "What floats", "Weapons", value: true, "Targets weapons."); FloatPlayers = BindBool(config, "What floats", "Players", value: true, "Targets players."); FloatEnemies = BindBool(config, "What floats", "Enemies", value: true, "Targets enemies."); UiEnabled = BindBool(config, "UI", "Enabled", value: true, "Shows the event HUD on this client."); StageStartChatEnabled = BindBool(config, "UI", "StageStartChatEnabled", value: true, "Broadcasts the stage summary, effect names, countdowns, and End notifications through vanilla chat."); CountdownEnabled = BindBool(config, "UI", "CountdownEnabled", value: true, "Enables the five-second start countdown and three-second end countdown. When disabled, effects start and end immediately after their announcements."); UiAnchor = config.Bind("UI", "Anchor", "BottomRight", 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())); UiAlignment = config.Bind("UI", "Alignment", "Right", new ConfigDescription("Horizontal alignment of the HUD text.", (AcceptableValueBase)(object)new AcceptableValueList(new string[3] { "Left", "Center", "Right" }), Array.Empty())); UiOffsetX = BindInt(config, "UI", "OffsetX", 0, -3840, 3840, "Horizontal offset from the anchor in pixels."); UiOffsetY = BindInt(config, "UI", "OffsetY", 0, -2160, 2160, "Vertical offset from the anchor in pixels."); UiScalePercent = BindInt(config, "UI", "ScalePercent", 70, 50, 200, "HUD scale percentage."); 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.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; RollEnabled.SettingChanged += ProbabilitySettingChanged; RollChance.SettingChanged += ProbabilitySettingChanged; VoidEnabled.SettingChanged += ProbabilitySettingChanged; VoidChance.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 StageEffect RollEffects() { 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); } } int num = Mathf.Clamp(MaxSimultaneousEffects.Value, 1, StageEffectSet.IndividualEffects.Count); if (list.Count > num) { for (int i = 0; i < num; i++) { int num2 = Random.Range(i, list.Count); List list2 = list; int index = i; int index2 = num2; StageEffect value = list[num2]; StageEffect value2 = list[i]; list2[index] = value; list[index2] = value2; } _logger.LogDebug((object)$"Effect roll produced {list.Count} successful effects; randomly limiting the event to {num}."); } StageEffect stageEffect = StageEffect.None; int num3 = Mathf.Min(list.Count, num); for (int j = 0; j < num3; j++) { stageEffect |= list[j]; } return stageEffect; } internal bool RollStageEnabled() { if (StageEffectChancePercent.Value < 100) { if (StageEffectChancePercent.Value > 0) { return Random.Range(1, 101) <= StageEffectChancePercent.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(FloatValuables.Value, FloatItems.Value, FloatDoors.Value, FloatWeapons.Value, FloatPlayers.Value, FloatEnemies.Value); } private void ProbabilitySettingChanged(object sender, EventArgs eventArgs) { LogProbabilityPolicy(); } private int GetChance(StageEffect effect) { return effect switch { StageEffect.Feather => FeatherEnabled.Value ? FeatherChance.Value : 0, StageEffect.ZeroGravity => ZeroGravityEnabled.Value ? ZeroGravityChance.Value : 0, StageEffect.Battery => BatteryEnabled.Value ? BatteryChance.Value : 0, StageEffect.Heal => HealEnabled.Value ? HealChance.Value : 0, StageEffect.Indestructible => IndestructibleEnabled.Value ? IndestructibleChance.Value : 0, StageEffect.Roll => RollEnabled.Value ? RollChance.Value : 0, StageEffect.Void => VoidEnabled.Value ? VoidChance.Value : 0, _ => 0, }; } 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 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] = 2.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 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 { None = 0, Feather = 1, ZeroGravity = 2, Battery = 4, Heal = 8, Indestructible = 0x10, Roll = 0x20, Void = 0x40 } internal static class StageEffectSet { internal const StageEffect All = StageEffect.Feather | StageEffect.ZeroGravity | StageEffect.Battery | StageEffect.Heal | StageEffect.Indestructible | StageEffect.Roll | StageEffect.Void; internal static readonly IReadOnlyList IndividualEffects = new StageEffect[7] { StageEffect.Feather, StageEffect.ZeroGravity, StageEffect.Battery, StageEffect.Heal, StageEffect.Indestructible, StageEffect.Roll, StageEffect.Void }; 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)) == 0; } internal static string Format(StageEffect effects, string separator, bool compactZeroGravity = false) { StringBuilder stringBuilder = new StringBuilder(); foreach (StageEffect individualEffect in IndividualEffects) { if (!Contains(effects, individualEffect)) { continue; } if (stringBuilder.Length > 0) { stringBuilder.Append(separator); } StringBuilder stringBuilder2 = stringBuilder; StageEffect stageEffect = individualEffect; string value; if (stageEffect != StageEffect.ZeroGravity) { if (stageEffect != StageEffect.Roll) { goto IL_005b; } value = "Roll"; } else { if (compactZeroGravity) { goto IL_005b; } value = "Zero Gravity"; } goto IL_006a; IL_005b: value = individualEffect.ToString(); goto IL_006a; IL_006a: stringBuilder2.Append(value); } if (stringBuilder.Length <= 0) { return "None"; } return stringBuilder.ToString(); } } internal readonly struct HudState { internal EventRunState State { get; } internal StageEffect Effect { get; } internal int DurationSeconds { get; } internal int IntervalSeconds { get; } internal int RemainingSeconds { get; } internal HudState(EventRunState state, StageEffect effect, int durationSeconds, int intervalSeconds, int remainingSeconds) { State = state; Effect = effect; DurationSeconds = durationSeconds; IntervalSeconds = intervalSeconds; RemainingSeconds = remainingSeconds; } } internal readonly struct TargetFilter { internal bool Valuables { 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 items, bool doors, bool weapons, bool players, bool enemies) { Valuables = valuables; Items = items; Doors = doors; Weapons = weapons; Players = players; Enemies = enemies; } internal bool Allows(PhysGrabObject physObject) { //IL_00b1: 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 || (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 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 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 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 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_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_0067: 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_0079: 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_004c: 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) try { Vector3 position = ((Component)player).transform.position; GameObject val; if (SemiFunc.IsMultiplayer()) { object[] array = new object[3] { 0.01f, 0.01f, 0.01f }; val = PhotonNetwork.Instantiate(_resolver.FeatherResourcePath, position, Quaternion.identity, (byte)0, array); } else { val = Object.Instantiate(_resolver.FeatherPrefab, position, Quaternion.identity); val.transform.localScale = Vector3.one * 0.01f; } ((Object)val).name = "StagePhysicsEvents_PlayerFeatherOrb"; val.AddComponent(); _coroutineOwner.StartCoroutine(InitializeFollowOrbAfterStart(val, player)); return val; } 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); 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_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_00a5: 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_0080: 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) foreach (FollowOrb value in _followOrbs.Values) { if (!((Object)(object)value.Orb == (Object)null) && !((Object)(object)value.Player == (Object)null)) { Vector3 position = ((Component)value.Player).transform.position; PhysGrabObject component = value.Orb.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; } value.Orb.transform.position = position; } } } 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); } if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient) { PhotonView component3 = orb.GetComponent(); if (component3 == null || component3.ViewID != 0) { PhotonNetwork.Destroy(orb); return; } } Object.Destroy((Object)(object)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_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0151: 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 VanillaEffectResolver _resolver; private readonly List _spawned = new List(); internal VoidStaffEffectAdapter(VanillaEffectResolver resolver) { _resolver = resolver; } internal bool EnsureAvailable() { return _resolver.ResolveVoid(); } internal void ApplyOnce(IReadOnlyList objects, IReadOnlyList players, int spawnCount) { //IL_00d1: 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_005a: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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) //IL_0077: 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 (!EnsureAvailable() || spawnCount <= 0) { return; } 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 num = 0; for (int i = 0; i < spawnCount; i++) { Vector3 val = list[i % list.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); num++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Could not create Void Staff effect at {val}: {ex.Message}"); } } StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Void Staff event created {num}/{spawnCount} effect(s) at random stage points."); } internal void Stop() { foreach (GameObject item in _spawned) { if ((Object)(object)item == (Object)null) { continue; } try { PhotonView val = item.GetComponent() ?? item.GetComponentInParent() ?? item.GetComponentInChildren(true); if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)val != (Object)null && val.ViewID != 0) { PhotonNetwork.Destroy(((Component)val).gameObject); } else if (!SemiFunc.IsMultiplayer()) { Object.Destroy((Object)(object)item); } } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Void effect cleanup deferred to scene unload: " + ex.Message)); } } _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 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) { _stageGeneration = stageGeneration; _stageMessage = stageMessage; _notifiedActors.Clear(); if (_config.StageStartChatEnabled.Value && !string.IsNullOrEmpty(_stageMessage) && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient) { _coroutineOwner.StartCoroutine(NotifyAllAfterCapabilityGrace(stageGeneration)); } } internal void PlayerEntered(Player player) { if (_config.StageStartChatEnabled.Value && !string.IsNullOrEmpty(_stageMessage) && PhotonNetwork.IsMasterClient) { _coroutineOwner.StartCoroutine(NotifyPlayerAfterCapabilityGrace(player, _stageGeneration)); } } internal void EndStage() { _stageGeneration++; _notifiedActors.Clear(); _stageMessage = string.Empty; } internal void EventStarted(StageEffect effect) { SendEventMessage(StageEffectSet.Format(effect, "/", compactZeroGravity: true)); } 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.StageStartChatEnabled.Value && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && !((Object)(object)GameDirector.instance == (Object)null) && (int)GameDirector.instance.currentState == 2) { ForceAllPlayersToSpeak(message); } } private static 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)) { try { item.ChatMessageSend(message); num++; } catch (Exception ex) { StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Stage event chat failed for player view {item.photonView.ViewID}: {ex.Message}"); } } } StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Stage event chat forced through {num} player avatar(s): {message}"); return num > 0; } } 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 int StateFormatVersion = 6; private const int PersistentEffectDurationSeconds = 300; private const int PersistentRefreshLeadSeconds = 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 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 int _fixedDurationSeconds; private int _fixedIntervalSeconds; private int _lastExtractionDirectorId; private int _lastExtractionCompletionCount = -1; private EventRunState _remoteState; private StageEffect _remoteEffect; private int _remoteDuration; private int _remoteInterval; private int _remoteEndTimestamp; private int _remoteStageToken; private Coroutine? _countdownCoroutine; private Coroutine? _endCountdownCoroutine; private int _countdownGeneration; private readonly HashSet _activeTargetIds = new HashSet(); private readonly Dictionary _playerAliveStates = new Dictionary(); private float _nextDynamicTargetScanAt; 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(_resolver); _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; _lastExtractionDirectorId = 0; _lastExtractionCompletionCount = -1; _remoteState = EventRunState.Inactive; PhysGrabObjectRegistry.Clear(); if (((Component)this).gameObject.activeSelf) { ((Component)this).gameObject.SetActive(false); } } internal void Shutdown() { StageEnding(); } 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)); hudState = new HudState(_state, _effect, _durationSeconds, _intervalSeconds, remainingSeconds); 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, _remoteDuration, _remoteInterval, remainingSeconds2); return true; } private void Update() { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Invalid comparison between Unknown and I4 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; } _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 && Time.time >= _stateEndsAt) { StageEffect stageEffect = (UsesFixedTiming() ? _fixedEffect : _config.RollEffects()); if (stageEffect == StageEffect.None || !CanApply(stageEffect)) { BeginWaiting(NextIntervalSeconds()); return; } int durationSeconds = (UsesFixedTiming() ? _fixedDurationSeconds : _config.RollDurationSeconds()); BeginCountdown(stageEffect, durationSeconds, NextIntervalSeconds()); } else { if (_state == EventRunState.Countdown || _state != EventRunState.Active) { return; } _feather.TickFollowOrbs(); _batteryOrb.Tick(); _healOrb.Tick(); _indestructibleOrb.Tick(); RefreshDynamicTargets(); if (_mode == EventMode.PersistentForStage) { if (Time.time >= _stateEndsAt - 10f) { RefreshPersistentEvent(); } } else if (_endCountdownCoroutine == null) { if (Time.time >= _stateEndsAt) { EndEventAndBeginWaiting(); } else if (_config.CountdownEnabled.Value && Time.time >= _stateEndsAt - 3f) { BeginEndCountdown(); } } } } private void FixedUpdate() { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: 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_00e2: 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_00ec: 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_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0123: 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_012b: 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; } bool result = true; foreach (StageEffect individualEffect in StageEffectSet.IndividualEffects) { if (StageEffectSet.Contains(effects, individualEffect) && individualEffect switch { StageEffect.Feather => _feather.EnsureAvailable() ? 1 : 0, StageEffect.ZeroGravity => _zeroGravity.EnsureAvailable() ? 1 : 0, StageEffect.Battery => _batteryOrb.EnsureAvailable(individualEffect) ? 1 : 0, StageEffect.Heal => _healOrb.EnsureAvailable(individualEffect) ? 1 : 0, StageEffect.Indestructible => _indestructibleOrb.EnsureAvailable(individualEffect) ? 1 : 0, StageEffect.Roll => _rollStaff.EnsureAvailable() ? 1 : 0, StageEffect.Void => _void.EnsureAvailable() ? 1 : 0, _ => 0, } == 0) { result = false; StagePhysicsEventsPlugin.ModLogger.LogWarning((object)$"Selected {individualEffect}, but its vanilla effect prefab is unavailable; the combined event cannot be applied."); } } return result; } private void PrepareStagePlan() { _mode = _config.CaptureMode(); _stageEffectsEnabled = _config.RollStageEnabled() && _config.HasSelectableEffect; _fixedEffect = StageEffect.None; _fixedDurationSeconds = 0; _fixedIntervalSeconds = 0; if (_stageEffectsEnabled && UsesStageFixedEffect()) { _fixedEffect = _config.RollEffects(); 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 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) { return; } 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 stageEffect = _config.RollEffects(); if (stageEffect != StageEffect.None && !CanApply(stageEffect)) { stageEffect = StageEffect.None; } _fixedEffect = stageEffect; StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Completed extraction #{num2}; FixedPerExtraction selected next effect: {_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) { StageEffect[] effects = new StageEffect[7] { StageEffect.Feather, StageEffect.ZeroGravity, StageEffect.Battery, StageEffect.Heal, StageEffect.Indestructible, StageEffect.Roll, StageEffect.Void }; for (int attempt = 0; attempt < 5; attempt++) { if (!_stageReady) { break; } if (generation != _stageGeneration) { break; } if (!IsAuthority()) { break; } bool flag = true; StageEffect[] array = effects; foreach (StageEffect stageEffect in array) { if ((UsesStageFixedEffect() ? StageEffectSet.Contains(_fixedEffect, stageEffect) : _config.IsEffectSelectable(stageEffect)) && !CanApply(stageEffect)) { flag = false; } } if (flag) { break; } yield return (object)new WaitForSeconds(1f); } } private void BeginWaiting(int intervalSeconds) { StopLocalEffects(); _state = EventRunState.Waiting; _effect = StageEffect.None; _durationSeconds = 0; _intervalSeconds = intervalSeconds; _stateEndsAt = Time.time + (float)intervalSeconds; Publish(_state, _effect, 0, intervalSeconds, intervalSeconds); StagePhysicsEventsPlugin.ModLogger.LogDebug((object)$"Waiting for {intervalSeconds}s before the next stage-event roll."); } private void BeginEvent(StageEffect effect, int durationSeconds, int nextIntervalSeconds) { _state = EventRunState.Active; _effect = effect; _durationSeconds = durationSeconds; _intervalSeconds = nextIntervalSeconds; _activeTargetFilter = _config.CaptureTargetFilter(); _stateEndsAt = Time.time + (float)durationSeconds; Publish(_state, effect, durationSeconds, nextIntervalSeconds, durationSeconds); ApplySelectedEffectsOnce(); StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Stage effect started: {effect}, duration={durationSeconds}s, next interval={nextIntervalSeconds}s."); } private void BeginCountdown(StageEffect effect, int durationSeconds, int nextIntervalSeconds) { StopLocalEffects(); if (!_config.CountdownEnabled.Value) { _notifier.EventStarted(effect); BeginEvent(effect, durationSeconds, nextIntervalSeconds); return; } _state = EventRunState.Countdown; _effect = effect; _durationSeconds = durationSeconds; _intervalSeconds = nextIntervalSeconds; int num = 8; _stateEndsAt = Time.time + (float)num; Publish(_state, effect, durationSeconds, nextIntervalSeconds, num); int countdownGeneration = ++_countdownGeneration; _countdownCoroutine = ((MonoBehaviour)this).StartCoroutine(RunCountdown(effect, durationSeconds, nextIntervalSeconds, _stageGeneration, countdownGeneration)); StagePhysicsEventsPlugin.ModLogger.LogInfo((object)$"Stage effect start countdown: {effect}, {5}s."); } private IEnumerator RunCountdown(StageEffect effect, int durationSeconds, int nextIntervalSeconds, int stageGeneration, int countdownGeneration) { if (!CountdownIsValid(stageGeneration, countdownGeneration)) { yield break; } _notifier.EventStarted(effect); yield return (object)new WaitForSeconds(3f); for (int seconds = 5; seconds >= 1; seconds--) { if (!CountdownIsValid(stageGeneration, countdownGeneration)) { yield break; } _notifier.Countdown(seconds); yield return (object)new WaitForSeconds(1f); } if (CountdownIsValid(stageGeneration, countdownGeneration)) { _notifier.Countdown(0); _countdownCoroutine = null; BeginEvent(effect, durationSeconds, nextIntervalSeconds); } } 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 CountdownIsValid(int stageGeneration, int countdownGeneration) { if (_stageReady && _stageGeneration == stageGeneration && _countdownGeneration == countdownGeneration && _state == EventRunState.Countdown && IsAuthority()) { return _config.Enabled.Value; } return false; } 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 { BeginCountdown(_fixedEffect, 300, 0); } } private void RefreshPersistentEvent() { _durationSeconds = 300; _stateEndsAt = Time.time + 300f; Publish(_state, _effect, _durationSeconds, 0, _durationSeconds); ApplySelectedEffectsOnce(); 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 ApplySelectedEffectsOnce() { List list = PhysGrabObjectRegistry.Snapshot(_activeTargetFilter); List list2 = CollectPlayers(); int num = 0; foreach (PhysGrabObject item in list) { 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={list.Count}, enemies={num}, players={(_activeTargetFilter.Players ? list2.Count : 0)}."); _activeTargetIds.Clear(); foreach (PhysGrabObject item2 in list) { if ((Object)(object)item2 != (Object)null) { _activeTargetIds.Add(((Object)item2).GetInstanceID()); } } ScheduleValuableProtection(); if (StageEffectSet.Contains(_effect, StageEffect.Feather)) { FeatherOrbEffectAdapter feather = _feather; IReadOnlyList players; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players = readOnlyList; } else { IReadOnlyList readOnlyList = list2; players = readOnlyList; } feather.ApplyOnce(list, players); } if (StageEffectSet.Contains(_effect, StageEffect.ZeroGravity)) { StaffAffectBroadcaster zeroGravity = _zeroGravity; IReadOnlyList players2; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players2 = readOnlyList; } else { IReadOnlyList readOnlyList = list2; players2 = readOnlyList; } zeroGravity.ApplyOnce(list, players2, _durationSeconds); } if (StageEffectSet.Contains(_effect, StageEffect.Battery)) { AdditionalOrbEffectAdapter batteryOrb = _batteryOrb; IReadOnlyList players3; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players3 = readOnlyList; } else { IReadOnlyList readOnlyList = list2; players3 = readOnlyList; } batteryOrb.ApplyOnce(StageEffect.Battery, list, players3); } if (StageEffectSet.Contains(_effect, StageEffect.Heal)) { AdditionalOrbEffectAdapter healOrb = _healOrb; IReadOnlyList players4; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players4 = readOnlyList; } else { IReadOnlyList readOnlyList = list2; players4 = readOnlyList; } healOrb.ApplyOnce(StageEffect.Heal, list, players4); } if (StageEffectSet.Contains(_effect, StageEffect.Indestructible)) { AdditionalOrbEffectAdapter indestructibleOrb = _indestructibleOrb; IReadOnlyList players5; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players5 = readOnlyList; } else { IReadOnlyList readOnlyList = list2; players5 = readOnlyList; } indestructibleOrb.ApplyOnce(StageEffect.Indestructible, list, players5); } if (StageEffectSet.Contains(_effect, StageEffect.Roll)) { StaffAffectBroadcaster rollStaff = _rollStaff; IReadOnlyList players6; if (!_activeTargetFilter.Players) { IReadOnlyList readOnlyList = Array.Empty(); players6 = readOnlyList; } else { IReadOnlyList readOnlyList = list2; players6 = readOnlyList; } rollStaff.ApplyOnce(list, players6, _durationSeconds); } if (StageEffectSet.Contains(_effect, StageEffect.Void)) { _void.ApplyOnce(list, list2, _config.RollVoidSpawnCount()); } CapturePlayerAliveStates(); _nextDynamicTargetScanAt = Time.time + 0.25f; } 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 RefreshDynamicValuableProtection() { if (!StageEffectSet.Contains(_effect, StageEffect.Roll) && !StageEffectSet.Contains(_effect, StageEffect.Void) && !StageEffectSet.Contains(_effect, StageEffect.ZeroGravity)) { return; } foreach (PhysGrabObject item in PhysGrabObjectRegistry.Snapshot(new TargetFilter(valuables: true, 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 (StageEffectSet.Contains(_effect, StageEffect.Roll) || StageEffectSet.Contains(_effect, StageEffect.Void) || StageEffectSet.Contains(_effect, StageEffect.ZeroGravity)) { _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() { bool flag = StageEffectSet.Contains(_effect, StageEffect.Roll); bool flag2 = StageEffectSet.Contains(_effect, StageEffect.Void); bool flag3 = flag || flag2; bool flag4 = StageEffectSet.Contains(_effect, StageEffect.ZeroGravity); if (flag3 || flag4) { List valuables = PhysGrabObjectRegistry.Snapshot(new TargetFilter(valuables: true, items: false, doors: false, weapons: false, players: false, enemies: false)); float startsAt = (flag3 ? Time.time : (_stateEndsAt - 0.5f)); float num = 0f; if (flag) { num = _config.RollIndestructibleReleaseDelaySeconds.Value; } if (flag2) { num = Mathf.Max(num, (float)_config.VoidIndestructibleReleaseDelaySeconds.Value); } if (flag4) { num = Mathf.Max(num, (float)_config.ZeroGravityIndestructibleReleaseDelaySeconds.Value); } float endsAt = _stateEndsAt + num; _valuableProtection.Schedule(valuables, startsAt, endsAt); } } 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++; if (_countdownCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_countdownCoroutine); _countdownCoroutine = null; } if (_endCountdownCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_endCountdownCoroutine); _endCountdownCoroutine = null; } _feather?.Stop(); _zeroGravity?.Stop(); _batteryOrb?.Stop(); _healOrb?.Stop(); _indestructibleOrb?.Stop(); _rollStaff?.Stop(); _void?.Stop(); _activeTargetIds.Clear(); _playerAliveStates.Clear(); _nextDynamicTargetScanAt = 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) { //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_0043: 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_0071: 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_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: 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"] = 6, [(object)"SPE.StageId"] = _stageToken, [(object)"SPE.State"] = (int)state, [(object)"SPE.Effect"] = (int)effect, [(object)"SPE.DurationSeconds"] = duration, [(object)"SPE.IntervalSeconds"] = interval, [(object)"SPE.EndServerTimestamp"] = num }; 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 != 6 || !TryGetInt(properties, "SPE.StageId", out _remoteStageToken) || !TryGetInt(properties, "SPE.State", out var value2) || !TryGetInt(properties, "SPE.Effect", out var value3) || !TryGetInt(properties, "SPE.DurationSeconds", out _remoteDuration) || !TryGetInt(properties, "SPE.IntervalSeconds", out _remoteInterval) || !TryGetInt(properties, "SPE.EndServerTimestamp", out _remoteEndTimestamp) || !Enum.IsDefined(typeof(EventRunState), value2) || !StageEffectSet.IsValid((StageEffect)value3)) { _remoteState = EventRunState.Inactive; return; } _remoteState = (EventRunState)value2; _remoteEffect = (StageEffect)value3; } 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; } 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 sealed class StagePhysicsEventHud : MonoBehaviour { private StagePhysicsConfig _config; private StagePhysicsEventController _controller; private GameObject? _root; private RectTransform? _rect; private TextMeshProUGUI? _text; private string _lastText = string.Empty; private string _lastLayout = string.Empty; private float _nextFontProbeAt; internal void Initialize(StagePhysicsConfig config, StagePhysicsEventController controller) { _config = config; _controller = controller; } private void Update() { EnsureCreated(); if ((Object)(object)_root == (Object)null || (Object)(object)_text == (Object)null) { return; } HudState hudState = default(HudState); bool flag = _config.UiEnabled.Value && _controller.TryGetHudState(out hudState); if (_root.activeSelf != flag) { _root.SetActive(flag); } if (flag) { ApplyLayout(); string text = BuildText(hudState); if (_lastText != text) { _lastText = text; ((TMP_Text)_text).text = text; } 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_00ad: 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_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Expected O, but got Unknown //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: 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("StagePhysicsEvents_HUD", new Type[1] { typeof(RectTransform) }); ((Object)_root).hideFlags = (HideFlags)61; _root.transform.SetParent(val, false); _lastLayout = string.Empty; _lastText = string.Empty; RectTransform component = _root.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.pivot = new Vector2(0.5f, 0.5f); component.offsetMin = Vector2.zero; component.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); } GameObject val2 = new GameObject("EventText", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }); val2.transform.SetParent(_root.transform, false); _rect = val2.GetComponent(); _rect.sizeDelta = new Vector2(700f, 360f); _text = val2.GetComponent(); ((TMP_Text)_text).fontSize = 28f; ((TMP_Text)_text).alignment = (TextAlignmentOptions)514; ((Graphic)_text).color = Color.white; ((TMP_Text)_text).outlineColor = Color32.op_Implicit(new Color(0f, 0f, 0f, 0.9f)); ((TMP_Text)_text).outlineWidth = 0.18f; ((Graphic)_text).raycastTarget = false; ((TMP_Text)_text).enableWordWrapping = false; _root.SetActive(false); } } private void ApplyLayout() { //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: 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_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_039c: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Unknown result type (might be due to invalid IL or missing references) string text = $"{_config.UiAnchor.Value}:{_config.UiAlignment.Value}:{_config.UiOffsetX.Value}:{_config.UiOffsetY.Value}:{_config.UiScalePercent.Value}"; if (!(_lastLayout == text) && !((Object)(object)_rect == (Object)null)) { _lastLayout = text; Vector2 val = (Vector2)(_config.UiAnchor.Value 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(0.5f, 1f), }); _rect.anchorMin = val; _rect.anchorMax = val; _rect.pivot = val; _rect.anchoredPosition = new Vector2((float)_config.UiOffsetX.Value, (float)_config.UiOffsetY.Value); ((Transform)_rect).localScale = Vector3.one * ((float)_config.UiScalePercent.Value / 100f); if ((Object)(object)_text != (Object)null) { TextMeshProUGUI text2 = _text; string value = _config.UiAlignment.Value; TextAlignmentOptions alignment = ((value == "Left") ? ((TextAlignmentOptions)513) : ((!(value == "Right")) ? ((TextAlignmentOptions)514) : ((TextAlignmentOptions)516))); ((TMP_Text)text2).alignment = alignment; } } } private void TryApplyRepoUiFont() { if ((Object)(object)_text == (Object)null || 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 && (Object)(object)val2 != (Object)(object)_text && (Object)(object)val2.font != (Object)null) { val = val2; break; } } } if ((Object)(object)val != (Object)null && (Object)(object)val.font != (Object)null && (Object)(object)((TMP_Text)_text).font != (Object)(object)val.font) { ((TMP_Text)_text).font = val.font; StagePhysicsEventsPlugin.ModLogger.LogDebug((object)("Applied R.E.P.O. UI font '" + ((Object)val.font).name + "' to the event HUD.")); } } private static string BuildText(HudState state) { if (state.State == EventRunState.Waiting) { return $"Waiting\nInterval: {state.IntervalSeconds}s"; } if (state.State == EventRunState.Countdown) { return "Starting\n" + StageEffectSet.Format(state.Effect, "\n"); } if (state.IntervalSeconds == 0) { return StageEffectSet.Format(state.Effect, "\n") + "\nMode: Persistent"; } return StageEffectSet.Format(state.Effect, "\n") + "\n" + $"Duration: {state.DurationSeconds}s"; } private void OnDestroy() { 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 void RunManagerChangeLevelPrefix() { StagePhysicsEventsPlugin.Instance?.Controller?.StageEnding(); } [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); } } }