using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Mirror; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("BubbleBoi")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BubbleBoi")] [assembly: AssemblyTitle("BubbleBoi")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BubbleBoi { [BepInPlugin("sbg.bubbleboi", "BubbleBoi", "0.2.2")] public sealed class Plugin : BaseUnityPlugin { public const string ModGuid = "sbg.bubbleboi"; public const string ModName = "BubbleBoi"; public const string ModVersion = "0.2.2"; internal static ManualLogSource Log; internal static Plugin Instance; internal ConfigEntry verboseLoggingConfig; internal ConfigEntry expiryWarningSecondsConfig; internal ConfigEntry expiryWarningEnabledConfig; private void Awake() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; verboseLoggingConfig = ((BaseUnityPlugin)this).Config.Bind("Diagnostics", "VerboseLogging", false, "Emit per-frame water-walk decision logs. Off by default — flip on when reporting issues so the log shows why synthetic ground was/wasn't injected."); expiryWarningEnabledConfig = ((BaseUnityPlugin)this).Config.Bind("Visuals", "ExpiryWarningEnabled", true, "Tint the local player's electromagnet shield orange→red in the final seconds before it expires."); expiryWarningSecondsConfig = ((BaseUnityPlugin)this).Config.Bind("Visuals", "ExpiryWarningSeconds", 3.5f, "Window before shield expiry over which the warning hue ramps in. The first half is orange-tinted, the last half ramps to red."); WaterSurfaceProxy.EnsureCreated(); new Harmony("sbg.bubbleboi").PatchAll(); Log.LogInfo((object)"BubbleBoi v0.2.2 loaded."); } } internal static class WaterSurfaceProxy { private static BoxCollider collider; internal static Collider Collider { get { EnsureCreated(); return (Collider)(object)collider; } } internal static void EnsureCreated() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_0026: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)collider != (Object)null)) { GameObject val = new GameObject("BubbleBoiWaterSurfaceProxy") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)val); collider = val.AddComponent(); ((Collider)collider).enabled = false; ((Collider)collider).isTrigger = false; collider.size = new Vector3(1f, 0.1f, 1f); } } } internal sealed class ShieldExpiryTinter : MonoBehaviour { private static readonly Color OrangeWarning = new Color(1f, 0.55f, 0.1f, 1f); private static readonly int[] ColorPropertyIds = new int[4] { Shader.PropertyToID("_TintColor"), Shader.PropertyToID("_Color"), Shader.PropertyToID("_BaseColor"), Shader.PropertyToID("_EmissionColor") }; private static ShieldExpiryTinter instance; private static MaterialPropertyBlock block; private PlayerInfo target; private double activationTimestamp; private Renderer[] cachedRenderers; private readonly Dictionary originalStartColors = new Dictionary(); private bool tintApplied; public static void OnLocalShieldActivated(PlayerInfo player) { EnsureInstance(); instance.target = player; instance.activationTimestamp = Time.timeAsDouble; instance.cachedRenderers = null; instance.originalStartColors.Clear(); instance.tintApplied = false; } private static void EnsureInstance() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_0026: Expected O, but got Unknown //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown if (!((Object)(object)instance != (Object)null)) { GameObject val = new GameObject("BubbleBoiShieldExpiryTinter") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)val); instance = val.AddComponent(); block = new MaterialPropertyBlock(); } } private void Update() { //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: 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_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null) { return; } if (!target.IsElectromagnetShieldActive) { if (tintApplied) { ResetTint(); } target = null; cachedRenderers = null; return; } Plugin plugin = Plugin.Instance; if ((Object)(object)plugin == (Object)null || !plugin.expiryWarningEnabledConfig.Value) { if (tintApplied) { ResetTint(); } return; } float num = (((Object)(object)GameManager.ItemSettings != (Object)null) ? GameManager.ItemSettings.ElectromagnetShieldDuration : 5f) - (float)(Time.timeAsDouble - activationTimestamp); float value = plugin.expiryWarningSecondsConfig.Value; if (num > value || num <= 0f) { if (tintApplied) { ResetTint(); } } else { float num2 = 1f - num / value; Color tint = ((num2 < 0.5f) ? Color.Lerp(Color.white, OrangeWarning, num2 * 2f) : Color.Lerp(OrangeWarning, Color.red, (num2 - 0.5f) * 2f)); EnsureRenderers(); ApplyTint(tint); tintApplied = true; } } private void EnsureRenderers() { if ((cachedRenderers == null || cachedRenderers.Length == 0) && !((Object)(object)target == (Object)null) && !((Object)(object)target.ElectromagnetShieldCollider == (Object)null)) { cachedRenderers = ((Component)target.ElectromagnetShieldCollider).GetComponentsInChildren(true); } } private void ApplyTint(Color tint) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (cachedRenderers == null) { return; } Renderer[] array = cachedRenderers; ParticleSystem val2 = default(ParticleSystem); foreach (Renderer val in array) { if ((Object)(object)val == (Object)null) { continue; } if (val is ParticleSystemRenderer && ((Component)val).TryGetComponent(ref val2)) { if (!originalStartColors.ContainsKey(val2)) { Dictionary dictionary = originalStartColors; ParticleSystem key = val2; MainModule main = val2.main; dictionary[key] = ((MainModule)(ref main)).startColor; } MainModule main2 = val2.main; ((MainModule)(ref main2)).startColor = new MinMaxGradient(tint); } else { val.GetPropertyBlock(block); int[] colorPropertyIds = ColorPropertyIds; foreach (int num in colorPropertyIds) { block.SetColor(num, tint); } val.SetPropertyBlock(block); } } } private void ResetTint() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (cachedRenderers != null) { Renderer[] array = cachedRenderers; foreach (Renderer val in array) { if (!((Object)(object)val == (Object)null) && !(val is ParticleSystemRenderer)) { val.SetPropertyBlock((MaterialPropertyBlock)null); } } } foreach (KeyValuePair originalStartColor in originalStartColors) { if (!((Object)(object)originalStartColor.Key == (Object)null)) { MainModule main = originalStartColor.Key.main; ((MainModule)(ref main)).startColor = originalStartColor.Value; } } originalStartColors.Clear(); tintApplied = false; } } [HarmonyPatch] internal static class WaterWalkPatches { private const float DecisionLogIntervalSeconds = 1f; private static readonly FieldInfo UprightColliderField = AccessTools.Field(typeof(PlayerMovement), "uprightCollider"); private static readonly FieldInfo GroundDataField = AccessTools.Field(typeof(PlayerMovement), "k__BackingField"); private static readonly MethodInfo NoClipEnabledGetter = AccessTools.PropertyGetter(typeof(PlayerMovement), "NoClipEnabled"); private static readonly Dictionary LastDecisionLogTimes = new Dictionary(); private static readonly Dictionary LastInjectedGroundState = new Dictionary(); [HarmonyPatch(typeof(PlayerMovement), "PerformGroundCheck")] [HarmonyPostfix] private static void PerformGroundCheckPostfix(PlayerMovement __instance, ref bool __result) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_008f: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0122: 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_016a: 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_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) if (!TryGetWaterSurfacePoint(__instance, out var waterPoint, out var reason)) { if (__result) { TrackInjectedGroundState(__instance, isInjected: false); } else { MaybeLogDecision(__instance, "synthetic ground skipped: " + reason); } TrackInjectedGroundState(__instance, isInjected: false); return; } if (__result) { Vector3 point = __instance.GroundData.point; if (point.y >= waterPoint.y - 0.05f) { MaybeLogDecision(__instance, $"keeping vanilla ground at y={point.y:F3} (water y={waterPoint.y:F3})"); TrackInjectedGroundState(__instance, isInjected: false); return; } MaybeLogDecision(__instance, $"vanilla ground submerged y={point.y:F3} < water y={waterPoint.y:F3} — overriding with synthetic surface"); } object? obj = UprightColliderField?.GetValue(__instance); CapsuleCollider val = (CapsuleCollider)((obj is CapsuleCollider) ? obj : null); if ((Object)(object)val == (Object)null) { MaybeLogDecision(__instance, "synthetic ground skipped: uprightCollider missing"); TrackInjectedGroundState(__instance, isInjected: false); return; } Vector3 val2 = ((Component)val).transform.TransformPoint(val.center); float num = val.center.y + 0.35f; if (val2.y - waterPoint.y > num) { MaybeLogDecision(__instance, $"synthetic ground skipped: water surface too far below ray origin ({val2.y - waterPoint.y:F3}m > {num:F3}m)"); TrackInjectedGroundState(__instance, isInjected: false); return; } __instance.NetworkgroundTerrainType = (GroundTerrainType)0; __instance.NetworkgroundTerrainDominantGlobalLayer = (TerrainLayer)0; GroundDataField?.SetValue(__instance, (object)new PlayerGroundData { point = waterPoint, contactPoint = waterPoint, normal = Vector3.up, collider = WaterSurfaceProxy.Collider, hasRigidbody = false, rigidbody = null }); bool flag = __result; __result = true; object[] obj2 = new object[4] { waterPoint.y, __instance.Position.y, flag, null }; PlayerInfo playerInfo = __instance.PlayerInfo; BoundsState? obj3; if (playerInfo == null) { obj3 = null; } else { LevelBoundsTracker levelBoundsTracker = playerInfo.LevelBoundsTracker; obj3 = ((levelBoundsTracker != null) ? new BoundsState?(levelBoundsTracker.AuthoritativeBoundsState) : null); } obj2[3] = obj3; MaybeLogDecision(__instance, string.Format("synthetic ground injected at waterY={0:F3}, playerY={1:F3}, replacedVanillaGround={2}, bounds={3}", obj2)); TrackInjectedGroundState(__instance, isInjected: true); } [HarmonyPatch(typeof(PlayerMovement), "FixedUpdate")] [HarmonyPostfix] private static void FixedUpdatePostfix(PlayerMovement __instance) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_008b: 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) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if (!TryGetWaterSurfacePoint(__instance, out var waterPoint, out var _)) { return; } PlayerInfo playerInfo = __instance.PlayerInfo; if ((Object)(object)((playerInfo != null) ? playerInfo.LevelBoundsTracker : null) == (Object)null) { return; } PlayerInfo playerInfo2 = __instance.PlayerInfo; Rigidbody val = ((playerInfo2 != null) ? playerInfo2.Rigidbody : null); if ((Object)(object)val == (Object)null || val.isKinematic) { return; } Vector3 position = val.position; if (!(position.y >= waterPoint.y)) { float num = waterPoint.y - position.y; position.y = waterPoint.y; val.position = position; Vector3 linearVelocity = val.linearVelocity; if (linearVelocity.y < 0f) { linearVelocity.y = 0f; val.linearVelocity = linearVelocity; } MaybeLogDecision(__instance, $"surface correction applied: raised by {num:F3}m to waterY={waterPoint.y:F3}", force: true); } } [HarmonyPatch(typeof(PlayerMovement), "OnServerBoundsStateChanged")] [HarmonyPrefix] private static bool OnServerBoundsStateChangedPrefix(PlayerMovement __instance, BoundsState currentState) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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) if (!BoundsStateExtensions.IsInOutOfBoundsHazard(currentState)) { return true; } if (!ShouldWaterWalk(__instance)) { MaybeLogDecision(__instance, $"server hazard passthrough: currentState={currentState}, reason=water-walk conditions not met"); return true; } PlayerInfo playerInfo = __instance.PlayerInfo; if (IsWaterHazard((playerInfo != null) ? playerInfo.LevelBoundsTracker : null, currentState)) { MaybeLogDecision(__instance, $"server water elimination suppressed: currentState={currentState}"); return false; } MaybeLogDecision(__instance, $"server hazard passthrough: currentState={currentState}, reason=hazard is not water"); return true; } private static bool TryGetWaterSurfacePoint(PlayerMovement movement, out Vector3 waterPoint, out string reason) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) waterPoint = default(Vector3); reason = string.Empty; if (!ShouldWaterWalkLocally(movement)) { reason = "local water-walk conditions not met"; return false; } PlayerInfo playerInfo = movement.PlayerInfo; LevelBoundsTracker val = ((playerInfo != null) ? playerInfo.LevelBoundsTracker : null); if ((Object)(object)val == (Object)null) { reason = "level bounds tracker missing"; return false; } if (!IsWaterHazard(val, val.AuthoritativeBoundsState) && !val.IsInOrOverOutOfBoundsHazard()) { reason = $"not over a water hazard (bounds={val.AuthoritativeBoundsState})"; return false; } waterPoint = movement.Position; waterPoint.y = val.CurrentOutOfBoundsHazardWorldHeightLocalOnly; return true; } private static bool ShouldWaterWalk(PlayerMovement movement) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)movement == (Object)null) { return false; } if ((Object)(object)movement.PlayerInfo == (Object)null || !movement.PlayerInfo.IsElectromagnetShieldActive) { return false; } GolfCartSeat activeGolfCartSeat = movement.PlayerInfo.ActiveGolfCartSeat; if (((GolfCartSeat)(ref activeGolfCartSeat)).IsValid()) { return false; } if ((bool)(NoClipEnabledGetter?.Invoke(null, null) ?? ((object)false))) { return false; } return true; } private static bool ShouldWaterWalkLocally(PlayerMovement movement) { if ((Object)(object)movement != (Object)null && ((NetworkBehaviour)movement).isLocalPlayer) { return ShouldWaterWalk(movement); } return false; } private static void TrackInjectedGroundState(PlayerMovement movement, bool isInjected) { if (!((Object)(object)movement == (Object)null)) { int instanceID = ((Object)movement).GetInstanceID(); if (!LastInjectedGroundState.TryGetValue(instanceID, out var value) || value != isInjected) { LastInjectedGroundState[instanceID] = isInjected; MaybeLogDecision(movement, isInjected ? "synthetic ground state changed: active" : "synthetic ground state changed: inactive", force: true); } } } private static void MaybeLogDecision(PlayerMovement movement, string message, bool force = false) { //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)movement == (Object)null || Plugin.Log == null || (Object)(object)Plugin.Instance == (Object)null || !Plugin.Instance.verboseLoggingConfig.Value) { return; } int instanceID = ((Object)movement).GetInstanceID(); double timeAsDouble = Time.timeAsDouble; if (force || !LastDecisionLogTimes.TryGetValue(instanceID, out var value) || !(timeAsDouble - value < 1.0)) { LastDecisionLogTimes[instanceID] = timeAsDouble; PlayerInfo playerInfo = movement.PlayerInfo; LevelBoundsTracker val = ((playerInfo != null) ? playerInfo.LevelBoundsTracker : null); ManualLogSource log = Plugin.Log; string[] obj = new string[12] { "[BubbleBoi] player=", ((playerInfo != null) ? ((Object)playerInfo).name : null) ?? ((Object)movement).name, " ", $"local={((NetworkBehaviour)movement).isLocalPlayer} shield={playerInfo != null && playerInfo.IsElectromagnetShieldActive} ", $"grounded={movement.IsGrounded} visible={movement.IsVisible} ", null, null, null, null, null, null, null }; int num; if (playerInfo == null) { num = 0; } else { GolfCartSeat activeGolfCartSeat = playerInfo.ActiveGolfCartSeat; num = (((GolfCartSeat)(ref activeGolfCartSeat)).IsValid() ? 1 : 0); } obj[5] = $"inCart={(byte)num != 0} "; obj[6] = "bounds="; object obj2; if (!((Object)(object)val != (Object)null)) { obj2 = "null"; } else { BoundsState authoritativeBoundsState = val.AuthoritativeBoundsState; obj2 = ((object)(BoundsState)(ref authoritativeBoundsState)).ToString(); } obj[7] = (string)obj2; obj[8] = " waterY="; obj[9] = (((Object)(object)val != (Object)null) ? val.CurrentOutOfBoundsHazardWorldHeightLocalOnly.ToString("F3") : "n/a"); obj[10] = " "; obj[11] = $"posY={movement.Position.y:F3} :: {message}"; log.LogInfo((object)string.Concat(obj)); } } [HarmonyPatch(typeof(PlayerInfo), "LocalPlayerActivateElectromagnetShield")] [HarmonyPostfix] private static void LocalPlayerActivateElectromagnetShieldPostfix(PlayerInfo __instance) { ShieldExpiryTinter.OnLocalShieldActivated(__instance); } private static bool IsWaterHazard(LevelBoundsTracker tracker, BoundsState boundsState) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Invalid comparison between Unknown and I4 if ((Object)(object)tracker == (Object)null) { return false; } if (BoundsStateExtensions.HasState(boundsState, (BoundsState)1)) { return (int)MainOutOfBoundsHazard.Type == 0; } if ((Object)(object)tracker.CurrentSecondaryHazardLocalOnly != (Object)null) { return (int)tracker.CurrentSecondaryHazardLocalOnly.Type == 0; } return (int)MainOutOfBoundsHazard.Type == 0; } } }