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 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.1.0")] [assembly: AssemblyInformationalVersion("0.2.1+af53bf0a480434b4286152982db8af13b820e15a")] [assembly: AssemblyProduct("FaerieFlight")] [assembly: AssemblyTitle("FaerieFlight")] [assembly: AssemblyVersion("0.2.1.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 IsTumblingRef = AccessTools.FieldRefAccess("isTumbling"); private static readonly FieldRef IsCrouchingRef = AccessTools.FieldRefAccess("isCrouching"); private static readonly FieldRef IsSlidingRef = AccessTools.FieldRefAccess("isSliding"); private static readonly FieldRef IsLocalRef = AccessTools.FieldRefAccess("isLocal"); private static readonly FieldRef ActiveRef = AccessTools.FieldRefAccess("active"); private static readonly FieldRef HideFlashlightRef = AccessTools.FieldRefAccess("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 byte FloatEventCode = 117; private static readonly byte[] RepoReservedEventCodes = new byte[3] { 123, 124, 199 }; private static readonly FieldRef AvatarTumbleRef = AccessTools.FieldRefAccess("tumble"); private static readonly FieldRef AvatarDeadSetRef = AccessTools.FieldRefAccess("deadSet"); private static readonly FieldRef AvatarIsDisabledRef = AccessTools.FieldRefAccess("isDisabled"); private static readonly FieldRef AvatarIsTumblingRef = AccessTools.FieldRefAccess("isTumbling"); private static readonly FieldRef AvatarIsLocalRef = AccessTools.FieldRefAccess("isLocal"); private static readonly FieldRef TumblePhysGrabObjectRef = AccessTools.FieldRefAccess("physGrabObject"); private static readonly FieldRef AffectTimerRef = AccessTools.FieldRefAccess("timer"); private static readonly FieldRef AffectTimerTotalRef = AccessTools.FieldRefAccess("timerTotal"); private GameObject? _affectPrefab; private bool _searched; private readonly Dictionary _active = new Dictionary(); 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 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; PhotonNetwork.AddCallbackTarget((object)this); } } private void OnDisable() { SceneManager.sceneLoaded -= OnSceneLoaded; PhotonNetwork.RemoveCallbackTarget((object)this); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { _affectPrefab = null; _searched = false; _active.Clear(); _nextBroadcast = 0f; Plugin.Log.LogInfo((object)("[FloatDiag] scene '" + ((Scene)(ref scene)).name + "' loaded -> reset prefab + per-player state")); } private void Update() { try { int num; object obj; if (Plugin.Enabled.Value) { num = (ShouldFloat() ? 1 : 0); if (num != 0) { obj = GetAffectPrefab(); goto IL_0020; } } else { num = 0; } obj = null; goto IL_0020; IL_0020: GameObject val = (GameObject)obj; GameDirector instance = GameDirector.instance; if (num == 0 || (Object)(object)val == (Object)null || (Object)(object)instance == (Object)null) { if (_active.Count > 0) { DestroyAllEffects(); } return; } MaintainEffects(); if (!SemiFunc.IsMultiplayer()) { SpawnMissingLocal(val, instance); } else if (SemiFunc.IsMasterClientOrSingleplayer()) { float time = Time.time; 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 list = null; foreach (KeyValuePair 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())).Add(item.Key); } else { AffectTimerRef.Invoke(value) = AffectTimerTotalRef.Invoke(value); } } if (list == null) { return; } foreach (PlayerAvatar item2 in list) { _active.Remove(item2); } } private void DestroyAllEffects() { foreach (KeyValuePair item in _active) { if ((Object)(object)item.Value != (Object)null) { Object.Destroy((Object)(object)((Component)item.Value).gameObject); } } _active.Clear(); } private void SpawnMissingLocal(GameObject prefab, GameDirector director) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_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) List playerList = director.PlayerList; for (int i = 0; i < playerList.Count; i++) { PlayerAvatar val = playerList[i]; if ((Object)(object)val == (Object)null || !IsAlive(val) || (_active.TryGetValue(val, out SemiAffect value) && (Object)(object)value != (Object)null)) { continue; } PlayerTumble val2 = AvatarTumbleRef.Invoke(val); if ((Object)(object)val2 == (Object)null) { continue; } PhysGrabObject val3 = TumblePhysGrabObjectRef.Invoke(val2); if (!((Object)(object)val3 == (Object)null)) { GameObject val4 = Object.Instantiate(prefab, ((Component)val).transform.position, Quaternion.identity); SemiAffect component = val4.GetComponent(); 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 playerList = director.PlayerList; List list = new List(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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) if (photonEvent.Code != 117 || PhotonNetwork.MasterClient == null || photonEvent.Sender != PhotonNetwork.MasterClient.ActorNumber || !(photonEvent.CustomData is int[] array)) { return; } 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) && IsAlive(val) && (!_active.TryGetValue(val, out SemiAffect value) || !((Object)(object)value != (Object)null))) { GameObject val2 = Object.Instantiate(affectPrefab, ((Component)val).transform.position, Quaternion.identity); SemiAffect component = val2.GetComponent(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val2); continue; } component.direction = Vector3.up; component.positionOfOriginalAreaOfEffect = ((Component)val).transform.position; component.Setup(num, 5f); _active[val] = component; } } } private GameObject? GetAffectPrefab() { if ((Object)(object)_affectPrefab != (Object)null) { return _affectPrefab; } StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null || instance.itemDictionary == null) { return null; } int num = 0; GameObject val = null; foreach (Item value in instance.itemDictionary.Values) { if ((Object)(object)value == (Object)null || value.prefab == null || !(value.itemName ?? "").ToLowerInvariant().Contains("gravity")) { continue; } num++; GameObject prefab; try { prefab = ((PrefabRef)(object)value.prefab).Prefab; } catch { continue; } if ((Object)(object)prefab == (Object)null) { continue; } PrefabRef val2 = ExtractProjectileRef(prefab); if (val2 == null) { continue; } GameObject prefab2; try { prefab2 = ((PrefabRef)(object)val2).Prefab; } catch { continue; } if ((Object)(object)prefab2 == (Object)null) { continue; } SemiAreaOfEffect componentInChildren = prefab2.GetComponentInChildren(true); if (!((Object)(object)componentInChildren == (Object)null) && componentInChildren.semiAffectPrefab != null) { GameObject prefab3; try { prefab3 = ((PrefabRef)(object)componentInChildren.semiAffectPrefab).Prefab; } catch { continue; } if ((Object)(object)prefab3 != (Object)null && (Object)(object)prefab3.GetComponent() != (Object)null) { val = prefab3; break; } } } if (!_searched || (Object)(object)val != (Object)null) { _searched = true; Plugin.Log.LogInfo((object)string.Format("[FloatDiag] prefab via item DB: gravityItems={0} -> {1}", num, ((Object)(object)val != (Object)null) ? "FOUND" : "not yet")); } _affectPrefab = val; return _affectPrefab; } private static PrefabRef? ExtractProjectileRef(GameObject itemGO) { ItemStaffZeroGravity component = itemGO.GetComponent(); if (!((Object)(object)component != (Object)null)) { return null; } return component.projectilePrefab; } private void MaybeLog(GameDirector director) { _logAccum += Time.deltaTime; if (_logAccum < 1f) { return; } _logAccum = 0f; bool flag = SemiFunc.IsMasterClientOrSingleplayer(); List 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}")); } } } } [BepInPlugin("darkharasho.FaerieFlight", "FaerieFlight", "0.2.1")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static ConfigEntry Enabled; internal static ConfigEntry Flashlight; private void Awake() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("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("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."); new Harmony("darkharasho.FaerieFlight").PatchAll(); ((Component)this).gameObject.AddComponent(); Log.LogInfo((object)"FaerieFlight v0.2.1 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.1"; } }