Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FaerieFlight v0.2.6
FaerieFlight.dll
Decompiled 3 days agousing 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 ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using UnityEngine; using UnityEngine.SceneManagement; [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("FaerieFlight")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.6.0")] [assembly: AssemblyInformationalVersion("0.2.6+f28d65039db0f6b3cd91fbb4364c06ba853f7391")] [assembly: AssemblyProduct("FaerieFlight")] [assembly: AssemblyTitle("FaerieFlight")] [assembly: AssemblyVersion("0.2.6.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 FaerieFlight { [HarmonyPatch(typeof(FlashlightController), "Update")] internal static class FlashlightTumblePatch { private static readonly FieldRef<PlayerAvatar, bool> IsTumblingRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isTumbling"); private static readonly FieldRef<PlayerAvatar, bool> IsCrouchingRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isCrouching"); private static readonly FieldRef<PlayerAvatar, bool> IsSlidingRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isSliding"); private static readonly FieldRef<PlayerAvatar, bool> IsLocalRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isLocal"); private static readonly FieldRef<FlashlightController, bool> ActiveRef = AccessTools.FieldRefAccess<FlashlightController, bool>("active"); private static readonly FieldRef<FlashlightController, bool> HideFlashlightRef = AccessTools.FieldRefAccess<FlashlightController, bool>("hideFlashlight"); private static float _logAccum; [HarmonyPrefix] private static void Prefix(FlashlightController __instance, ref FlashState __state) { __state = default(FlashState); try { if (Plugin.Enabled.Value && Plugin.Flashlight.Value && !((Object)(object)RunManager.instance == (Object)null) && !SemiFunc.MenuLevel() && SemiFunc.RunIsLevel()) { PlayerAvatar playerAvatar = __instance.PlayerAvatar; if (!((Object)(object)playerAvatar == (Object)null) && IsLocalRef.Invoke(playerAvatar)) { __state.masked = true; __state.tumbling = IsTumblingRef.Invoke(playerAvatar); __state.crouching = IsCrouchingRef.Invoke(playerAvatar); __state.sliding = IsSlidingRef.Invoke(playerAvatar); IsTumblingRef.Invoke(playerAvatar) = false; IsCrouchingRef.Invoke(playerAvatar) = false; IsSlidingRef.Invoke(playerAvatar) = false; } } } catch (Exception arg) { Plugin.Log.LogError((object)$"FlashlightTumblePatch.Prefix: {arg}"); } } [HarmonyPostfix] private static void Postfix(FlashlightController __instance, FlashState __state) { if (!__state.masked) { return; } try { PlayerAvatar playerAvatar = __instance.PlayerAvatar; if ((Object)(object)playerAvatar != (Object)null) { IsTumblingRef.Invoke(playerAvatar) = __state.tumbling; IsCrouchingRef.Invoke(playerAvatar) = __state.crouching; IsSlidingRef.Invoke(playerAvatar) = __state.sliding; } _logAccum += Time.deltaTime; if (_logAccum >= 1f) { _logAccum = 0f; Plugin.Log.LogInfo((object)($"[FloatDiag] flashlight active={ActiveRef.Invoke(__instance)} " + $"LightActive={__instance.LightActive} hide={HideFlashlightRef.Invoke(__instance)} " + $"wasTumbling={__state.tumbling} wasCrouching={__state.crouching}")); } } catch (Exception arg) { Plugin.Log.LogError((object)$"FlashlightTumblePatch.Postfix: {arg}"); } } } internal struct FlashState { public bool masked; public bool tumbling; public bool crouching; public bool sliding; } public class FloatDriver : MonoBehaviour, IOnEventCallback { private const float AffectTime = 5f; private const float RefreshInterval = 4f; private const float PrefabScanCooldown = 2f; private const float PrefabScanSettleDelay = 1.5f; private const float GrabOverrideTime = 0.2f; private const float ClimbSuppressTime = 0.5f; private const float MissingEffectRebroadcastDelay = 0.25f; private const string StaffItemKey = "Item Staff Zero Gravity"; private const byte FloatEventCode = 117; private static readonly byte[] RepoReservedEventCodes = new byte[3] { 123, 124, 199 }; private static readonly FieldRef<PlayerAvatar, PlayerTumble> AvatarTumbleRef = AccessTools.FieldRefAccess<PlayerAvatar, PlayerTumble>("tumble"); private static readonly FieldRef<PlayerAvatar, bool> AvatarDeadSetRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("deadSet"); private static readonly FieldRef<PlayerAvatar, bool> AvatarIsDisabledRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isDisabled"); private static readonly FieldRef<PlayerAvatar, bool> AvatarIsTumblingRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isTumbling"); private static readonly FieldRef<PlayerAvatar, bool> AvatarIsLocalRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isLocal"); private static readonly FieldRef<PlayerTumble, PhysGrabObject> TumblePhysGrabObjectRef = AccessTools.FieldRefAccess<PlayerTumble, PhysGrabObject>("physGrabObject"); private static readonly FieldRef<PhysGrabber, PhysGrabObject> GrabbedPhysGrabObjectRef = AccessTools.FieldRefAccess<PhysGrabber, PhysGrabObject>("grabbedPhysGrabObject"); private static readonly FieldRef<LoadingUI, bool> LevelAnimationCompletedRef = AccessTools.FieldRefAccess<LoadingUI, bool>("levelAnimationCompleted"); private static readonly FieldRef<SemiAffect, float> AffectTimerRef = AccessTools.FieldRefAccess<SemiAffect, float>("timer"); private static readonly FieldRef<SemiAffect, float> AffectTimerTotalRef = AccessTools.FieldRefAccess<SemiAffect, float>("timerTotal"); private GameObject? _affectPrefab; private bool _searched; private float _nextPrefabScan; private bool _photonCallbackRegistered; private readonly Dictionary<PlayerAvatar, SemiAffect> _active = new Dictionary<PlayerAvatar, SemiAffect>(); private float _nextBroadcast; private float _logAccum; private static bool ShouldFloat() { if ((Object)(object)RunManager.instance == (Object)null) { return false; } if (SemiFunc.MenuLevel()) { return false; } return SemiFunc.RunIsLevel(); } private static bool IsAlive(PlayerAvatar pa) { if (!AvatarDeadSetRef.Invoke(pa)) { return !AvatarIsDisabledRef.Invoke(pa); } return false; } private static bool LevelFullyLoaded() { LoadingUI instance = LoadingUI.instance; if ((Object)(object)instance != (Object)null) { return LevelAnimationCompletedRef.Invoke(instance); } return false; } private void OnEnable() { if (Array.IndexOf(RepoReservedEventCodes, (byte)117) >= 0) { Plugin.Log.LogError((object)($"FloatEventCode {(byte)117} collides with a REPO-reserved event (kick/ban); " + "networked float disabled to avoid kicking clients. Pick an unreserved code.")); ((Behaviour)this).enabled = false; } else { SceneManager.sceneLoaded += OnSceneLoaded; } } private void OnDisable() { SceneManager.sceneLoaded -= OnSceneLoaded; UnregisterPhotonCallback(); } private void RegisterPhotonCallback() { if (!_photonCallbackRegistered) { PhotonNetwork.AddCallbackTarget((object)this); _photonCallbackRegistered = true; } } private void UnregisterPhotonCallback() { if (_photonCallbackRegistered) { PhotonNetwork.RemoveCallbackTarget((object)this); _photonCallbackRegistered = false; } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { _affectPrefab = null; _searched = false; _active.Clear(); _nextBroadcast = 0f; _nextPrefabScan = Time.time + 1.5f; Plugin.Log.LogInfo((object)("[FloatDiag] scene '" + ((Scene)(ref scene)).name + "' loaded -> reset prefab + per-player state")); } private void Update() { try { bool flag = FloatGate.ShouldRunFloatWork(Plugin.Enabled.Value, ShouldFloat(), LevelFullyLoaded()); GameObject val = (flag ? GetAffectPrefab() : null); GameDirector instance = GameDirector.instance; if (!flag || (Object)(object)val == (Object)null || (Object)(object)instance == (Object)null) { UnregisterPhotonCallback(); if (_active.Count > 0) { DestroyAllEffects(); } return; } if (FloatGate.PhotonCallbackDesired(flag, SemiFunc.IsMultiplayer())) { RegisterPhotonCallback(); } MaintainEffects(); SuppressClimbWhileFloating(); if (SemiFunc.IsMasterClientOrSingleplayer()) { CompensateGrabStrength(instance); } if (!SemiFunc.IsMultiplayer()) { SpawnMissingLocal(val, instance); } else if (SemiFunc.IsMasterClientOrSingleplayer()) { float time = Time.time; _nextBroadcast = FloatGate.NextBroadcastTime(_nextBroadcast, time, AnyEligiblePlayerMissingEffect(instance), 0.25f); if (time >= _nextBroadcast) { _nextBroadcast = time + 4f; BroadcastRoster(instance); } } MaybeLog(instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"FloatDriver.Update: {arg}"); } } private void MaintainEffects() { if (_active.Count == 0) { return; } List<PlayerAvatar> list = null; foreach (KeyValuePair<PlayerAvatar, SemiAffect> item in _active) { PlayerAvatar key = item.Key; SemiAffect value = item.Value; if ((Object)(object)key == (Object)null || (Object)(object)value == (Object)null || !IsAlive(key)) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } (list ?? (list = new List<PlayerAvatar>())).Add(item.Key); } else { AffectTimerRef.Invoke(value) = AffectTimerTotalRef.Invoke(value); } } if (list == null) { return; } foreach (PlayerAvatar item2 in list) { _active.Remove(item2); } } private void SuppressClimbWhileFloating() { foreach (KeyValuePair<PlayerAvatar, SemiAffect> item in _active) { PlayerAvatar key = item.Key; if (!((Object)(object)key == (Object)null) && !((Object)(object)item.Value == (Object)null) && AvatarIsLocalRef.Invoke(key)) { if (FloatGate.ShouldSuppressClimb(Plugin.DisableWallClimbWhileFloating.Value, localEffectActive: true) && (Object)(object)key.physGrabber != (Object)null) { key.physGrabber.OverrideDisableSpecialGrabPowers(0.5f); } break; } } } private bool AnyEligiblePlayerMissingEffect(GameDirector director) { List<PlayerAvatar> playerList = director.PlayerList; for (int i = 0; i < playerList.Count; i++) { PlayerAvatar val = playerList[i]; if (!((Object)(object)val == (Object)null) && IsAlive(val)) { PlayerTumble val2 = AvatarTumbleRef.Invoke(val); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)TumblePhysGrabObjectRef.Invoke(val2) == (Object)null) && (!_active.TryGetValue(val, out SemiAffect value) || (Object)(object)value == (Object)null)) { return true; } } } return false; } private void DestroyAllEffects() { foreach (KeyValuePair<PlayerAvatar, SemiAffect> item in _active) { if ((Object)(object)item.Value != (Object)null) { Object.Destroy((Object)(object)((Component)item.Value).gameObject); } } _active.Clear(); } private void CompensateGrabStrength(GameDirector director) { if (!Plugin.RestoreGrabUpgrades.Value) { return; } List<PlayerAvatar> playerList = director.PlayerList; for (int i = 0; i < playerList.Count; i++) { PlayerAvatar val = playerList[i]; if ((Object)(object)val == (Object)null || (Object)(object)val.physGrabber == (Object)null) { continue; } PhysGrabObject val2 = GrabbedPhysGrabObjectRef.Invoke(val.physGrabber); if (!((Object)(object)val2 == (Object)null)) { SemiAffect value; float? num = FloatGate.CompensatedGrabStrength(_active.TryGetValue(val, out value) && (Object)(object)value != (Object)null, (Object)(object)((Component)val2).GetComponent<EnemyRigidbody>() != (Object)null, val.physGrabber.grabStrength, Plugin.StrengthSoftening.Value); if (num.HasValue) { float valueOrDefault = num.GetValueOrDefault(); val2.OverrideGrabStrength(valueOrDefault, 0.2f); } } } } private void SpawnMissingLocal(GameObject prefab, GameDirector director) { //IL_0096: 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_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_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) List<PlayerAvatar> playerList = director.PlayerList; for (int i = 0; i < playerList.Count; i++) { PlayerAvatar val = playerList[i]; if ((Object)(object)val == (Object)null) { continue; } PlayerTumble val2 = AvatarTumbleRef.Invoke(val); PhysGrabObject val3 = (((Object)(object)val2 != (Object)null) ? TumblePhysGrabObjectRef.Invoke(val2) : null); if (FloatGate.CanSpawnEffect(Plugin.Enabled.Value, ShouldFloat(), IsAlive(val), (Object)(object)val2 != (Object)null, (Object)(object)val3 != (Object)null, _active.TryGetValue(val, out SemiAffect value) && (Object)(object)value != (Object)null)) { GameObject val4 = Object.Instantiate<GameObject>(prefab, ((Component)val).transform.position, Quaternion.identity); SemiAffect component = val4.GetComponent<SemiAffect>(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val4); continue; } component.direction = Vector3.up; component.positionOfOriginalAreaOfEffect = ((Component)val).transform.position; component.SetupSingleplayer(((Component)val).transform, val3, 5f, val); _active[val] = component; } } } private void BroadcastRoster(GameDirector director) { //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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown if (!PhotonNetwork.InRoom) { return; } List<PlayerAvatar> playerList = director.PlayerList; List<int> list = new List<int>(playerList.Count); for (int i = 0; i < playerList.Count; i++) { PlayerAvatar val = playerList[i]; if (!((Object)(object)val == (Object)null) && IsAlive(val) && !((Object)(object)val.photonView == (Object)null)) { list.Add(val.photonView.ViewID); } } if (list.Count != 0) { PhotonNetwork.RaiseEvent((byte)117, (object)list.ToArray(), new RaiseEventOptions { Receivers = (ReceiverGroup)1 }, SendOptions.SendReliable); } } public void OnEvent(EventData photonEvent) { //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) if (photonEvent.Code != 117) { return; } try { if (PhotonNetwork.MasterClient == null || photonEvent.Sender != PhotonNetwork.MasterClient.ActorNumber || !(photonEvent.CustomData is int[] array) || !FloatGate.ShouldRunFloatWork(Plugin.Enabled.Value, ShouldFloat(), LevelFullyLoaded())) { return; } bool value = Plugin.Enabled.Value; bool inFloatableLevel = ShouldFloat(); GameObject affectPrefab = GetAffectPrefab(); if ((Object)(object)affectPrefab == (Object)null) { return; } int[] array2 = array; foreach (int num in array2) { PlayerAvatar val = SemiFunc.PlayerAvatarGetFromPhotonID(num); if ((Object)(object)val == (Object)null) { continue; } PlayerTumble val2 = AvatarTumbleRef.Invoke(val); PhysGrabObject val3 = (((Object)(object)val2 != (Object)null) ? TumblePhysGrabObjectRef.Invoke(val2) : null); if (FloatGate.CanSpawnEffect(value, inFloatableLevel, IsAlive(val), (Object)(object)val2 != (Object)null, (Object)(object)val3 != (Object)null, _active.TryGetValue(val, out SemiAffect value2) && (Object)(object)value2 != (Object)null)) { GameObject val4 = Object.Instantiate<GameObject>(affectPrefab, ((Component)val).transform.position, Quaternion.identity); SemiAffect component = val4.GetComponent<SemiAffect>(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val4); continue; } component.direction = Vector3.up; component.positionOfOriginalAreaOfEffect = ((Component)val).transform.position; component.Setup(num, 5f); _active[val] = component; } } } catch (Exception arg) { Plugin.Log.LogError((object)$"FloatDriver.OnEvent: {arg}"); } } private GameObject? GetAffectPrefab() { if ((Object)(object)_affectPrefab != (Object)null) { return _affectPrefab; } if (!FloatGate.ShouldScanPrefab((Object)(object)_affectPrefab != (Object)null, Time.time, _nextPrefabScan)) { return null; } _nextPrefabScan = Time.time + 2f; StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null || instance.itemDictionary == null) { return null; } GameObject val = null; int num = 0; Item value; bool flag = instance.itemDictionary.TryGetValue("Item Staff Zero Gravity", out value) && (Object)(object)(val = TryResolveAffect(value)) != (Object)null; if ((Object)(object)val == (Object)null) { foreach (Item value2 in instance.itemDictionary.Values) { if (!((Object)(object)value2 == (Object)null) && value2.prefab != null && (value2.itemName ?? "").ToLowerInvariant().Contains("gravity")) { num++; val = TryResolveAffect(value2); if ((Object)(object)val != (Object)null) { break; } } } } if (!_searched || (Object)(object)val != (Object)null) { _searched = true; string text = (flag ? "via staff key" : $"via item DB: gravityItems={num}"); Plugin.Log.LogInfo((object)("[FloatDiag] prefab " + text + " -> " + (((Object)(object)val != (Object)null) ? "FOUND" : "not yet"))); } _affectPrefab = val; return _affectPrefab; } private static PrefabRef? ExtractProjectileRef(GameObject itemGO) { ItemStaffZeroGravity component = itemGO.GetComponent<ItemStaffZeroGravity>(); if (!((Object)(object)component != (Object)null)) { return null; } return component.projectilePrefab; } private static GameObject? TryResolveAffect(Item? item) { if ((Object)(object)item == (Object)null || item.prefab == null) { return null; } GameObject prefab; try { prefab = ((PrefabRef<GameObject>)(object)item.prefab).Prefab; } catch { return null; } if ((Object)(object)prefab == (Object)null) { return null; } PrefabRef val = ExtractProjectileRef(prefab); if (val == null) { return null; } GameObject prefab2; try { prefab2 = ((PrefabRef<GameObject>)(object)val).Prefab; } catch { return null; } if ((Object)(object)prefab2 == (Object)null) { return null; } SemiAreaOfEffect componentInChildren = prefab2.GetComponentInChildren<SemiAreaOfEffect>(true); if ((Object)(object)componentInChildren == (Object)null || componentInChildren.semiAffectPrefab == null) { return null; } GameObject prefab3; try { prefab3 = ((PrefabRef<GameObject>)(object)componentInChildren.semiAffectPrefab).Prefab; } catch { return null; } if (!((Object)(object)prefab3 != (Object)null) || !((Object)(object)prefab3.GetComponent<SemiAffectZeroGravity>() != (Object)null)) { return null; } return prefab3; } private void MaybeLog(GameDirector director) { _logAccum += Time.deltaTime; if (_logAccum < 1f) { return; } _logAccum = 0f; bool flag = SemiFunc.IsMasterClientOrSingleplayer(); List<PlayerAvatar> playerList = director.PlayerList; for (int i = 0; i < playerList.Count; i++) { PlayerAvatar val = playerList[i]; if (!((Object)(object)val == (Object)null)) { SemiAffect value; bool flag2 = _active.TryGetValue(val, out value) && (Object)(object)value != (Object)null; Plugin.Log.LogInfo((object)($"[FloatDiag] master={flag} p{i} local={AvatarIsLocalRef.Invoke(val)} " + $"alive={IsAlive(val)} tumbling={AvatarIsTumblingRef.Invoke(val)} effect={flag2}")); } } } } public static class FloatGate { public static bool CanSpawnEffect(bool modEnabled, bool inFloatableLevel, bool avatarAlive, bool tumbleLinked, bool physGrabObjectLinked, bool effectAlreadyActive) { if (modEnabled && inFloatableLevel && avatarAlive && tumbleLinked && physGrabObjectLinked) { return !effectAlreadyActive; } return false; } public static bool ShouldScanPrefab(bool prefabCached, float now, float nextScanAllowedAt) { if (!prefabCached) { return now >= nextScanAllowedAt; } return false; } public static bool ShouldRunFloatWork(bool modEnabled, bool inFloatableLevel, bool levelFullyLoaded) { return modEnabled && inFloatableLevel && levelFullyLoaded; } public static bool PhotonCallbackDesired(bool floatWorkActive, bool isMultiplayer) { return floatWorkActive && isMultiplayer; } public static bool ShouldSuppressClimb(bool disableClimbWhileFloating, bool localEffectActive) { return disableClimbWhileFloating && localEffectActive; } public static float NextBroadcastTime(float scheduledAt, float now, bool eligiblePlayerMissingEffect, float missingEffectDelay) { if (!eligiblePlayerMissingEffect) { return scheduledAt; } float num = now + missingEffectDelay; if (!(num < scheduledAt)) { return scheduledAt; } return num; } public static float? CompensatedGrabStrength(bool effectActive, bool grabbedIsEnemy, float upgradedStrength, float softening) { if (!effectActive || grabbedIsEnemy) { return null; } if (upgradedStrength <= 1f) { return null; } float num = ((softening < 0f) ? 0f : ((softening > 1f) ? 1f : softening)); float num2 = (float)Math.Sqrt(upgradedStrength); return upgradedStrength + (num2 - upgradedStrength) * num; } } [BepInPlugin("darkharasho.FaerieFlight", "FaerieFlight", "0.2.6")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> Flashlight; internal static ConfigEntry<bool> RestoreGrabUpgrades; internal static ConfigEntry<float> StrengthSoftening; internal static ConfigEntry<bool> DisableWallClimbWhileFloating; private void Awake() { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master on/off switch for permanent floating. Best set on the host; the host drives the float for every player. (All players should run the mod.)"); Flashlight = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Flashlight", true, "Keep your flashlight on while floating. The game normally turns it off during tumble; since it's a dark game, this keeps it lit."); RestoreGrabUpgrades = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RestoreGrabUpgrades", true, "The game ignores Strength upgrades while a player is tumbling, so permanent float would leave them permanently dead. Restores them (softened) while floating. Host-side setting; grabbing enemies keeps vanilla behavior either way."); StrengthSoftening = ((BaseUnityPlugin)this).Config.Bind<float>("General", "StrengthSoftening", 0.65f, new ConfigDescription("How much to soften restored Strength upgrades while floating: 0 = full upgrade strength, 1 = square-root curve. Floating grabbers take the grab's recoil, so full strength can fling you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); DisableWallClimbWhileFloating = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DisableWallClimbWhileFloating", true, "The Tumble Climb upgrade turns any wall grab into a climb; while floating, the climb spring yanks you out of the air to the grab point (feels like being dragged down to the ground). Suppresses climbing while you float — wall grabs show the game's 'disabled' zap instead. Per-player setting; turn off to keep wall-climbing."); new Harmony("darkharasho.FaerieFlight").PatchAll(); ((Component)this).gameObject.AddComponent<FloatDriver>(); Log.LogInfo((object)"FaerieFlight v0.2.6 loaded."); } } public static class PluginInfo { public const string PLUGIN_GUID = "darkharasho.FaerieFlight"; public const string PLUGIN_NAME = "FaerieFlight"; public const string PLUGIN_VERSION = "0.2.6"; } }