using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BoneLib; using BoneLib.BoneMenu; using FusionSyringe; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.Combat; using Il2CppSLZ.Marrow.Warehouse; using LabFusion.Entities; using LabFusion.Network; using LabFusion.Network.Serialization; using LabFusion.Player; using LabFusion.Preferences.Server; using LabFusion.SDK.Modules; using LabFusion.Senders; using MelonLoader; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Core), "Fusion Syringe", "1.0.1", "RusMenu", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("0.0.0.0")] namespace FusionSyringe; public sealed class Core : MelonMod { public const float SedationSeconds = 20f; public const string SyringeBarcode = "Bugrax.BLSyringe.Spawnable.SyringeTest"; private static float _sedatedUntil; private static readonly Dictionary HitCooldowns = new Dictionary(); private bool _crateRenamed; private bool _menuReady; internal static bool NativeMode; private static bool _rulesSaved; private static bool _oldKnockout; private static bool _oldMortality; private static bool _oldFriendly; private static int _oldLength; internal static Core Instance { get; private set; } internal static bool IsSedated => Time.unscaledTime < _sedatedUntil; private static void RestoreRules() { if (_rulesSaved) { SavedServerSettings.Knockout.Value = _oldKnockout; SavedServerSettings.Mortality.Value = _oldMortality; SavedServerSettings.FriendlyFire.Value = _oldFriendly; SavedServerSettings.KnockoutLength.Value = _oldLength; _rulesSaved = false; } } private static void SelectMode(bool native) { if (native && !NetworkInfo.IsHost) { Core instance = Instance; if (instance != null) { ((MelonBase)instance).LoggerInstance.Warning("Native mode requires hosting an active Fusion lobby."); } return; } NativeMode = native; if (!native) { RestoreRules(); } HitCooldowns.Clear(); Core instance2 = Instance; if (instance2 != null) { ((MelonBase)instance2).LoggerInstance.Msg(native ? "Mode: native Fusion (host only)" : "Mode: DLL on participants"); } } public override void OnInitializeMelon() { Instance = this; ModuleMessageManager.RegisterHandler(); ((MelonBase)this).LoggerInstance.Msg("Fusion Syringe initialized. Effect duration: 20 seconds."); } public override void OnUpdate() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) TryRenameCrate(); if (!_menuReady && Page.Root != null) { Page obj = Page.Root.CreatePage("Fusion Syringe", Color.cyan, 0, true); obj.CreateFunction("DLL у игроков (по умолчанию)", Color.green, (Action)delegate { SelectMode(native: false); }); obj.CreateFunction("Без DLL у остальных [Хост]", Color.yellow, (Action)delegate { SelectMode(native: true); }); _menuReady = true; } if (NativeMode && !NetworkInfo.IsHost) { SelectMode(native: false); } if (NativeMode && NetworkInfo.IsHost && SavedServerSettings.Knockout != null) { if (!_rulesSaved) { _oldKnockout = SavedServerSettings.Knockout.Value; _oldMortality = SavedServerSettings.Mortality.Value; _oldFriendly = SavedServerSettings.FriendlyFire.Value; _oldLength = SavedServerSettings.KnockoutLength.Value; _rulesSaved = true; } if (!SavedServerSettings.Knockout.Value) { SavedServerSettings.Knockout.Value = true; } if (!SavedServerSettings.Mortality.Value) { SavedServerSettings.Mortality.Value = true; } if (!SavedServerSettings.FriendlyFire.Value) { SavedServerSettings.FriendlyFire.Value = true; } if (SavedServerSettings.KnockoutLength.Value != 20) { SavedServerSettings.KnockoutLength.Value = 20; } } if (IsSedated && !((Object)(object)Player.RigManager == (Object)null)) { RemapRig remapHeptaRig = Player.RigManager.remapHeptaRig; if ((Object)(object)remapHeptaRig != (Object)null) { remapHeptaRig._currentVelocity = Vector2.op_Implicit(Vector3.zero); remapHeptaRig._currentAcceleration = Vector2.op_Implicit(Vector3.zero); } } } public override void OnDeinitializeMelon() { RestoreRules(); _sedatedUntil = 0f; HitCooldowns.Clear(); Instance = null; } internal static void SedateLocal(float seconds = 20f) { if (float.IsFinite(seconds) && !(seconds <= 0f) && !IsSedated && !LocalRagdoll.KnockedOut) { float num = Mathf.Clamp(seconds, 0f, 20f); LocalRagdoll.Knockout(num, false); _sedatedUntil = Mathf.Max(_sedatedUntil, Time.unscaledTime + num); Core instance = Instance; if (instance != null) { ((MelonBase)instance).LoggerInstance.Msg($"Local player sedated for {num:0} seconds."); } } } internal static void OnSyringeStab(StabPoint stabPoint, Transform syringeTransform) { if ((NativeMode && NetworkInfo.HasServer && !NetworkInfo.IsHost) || stabPoint == null || (Object)(object)syringeTransform == (Object)null || !IsFusionSyringe(syringeTransform) || stabPoint.stabJoints == null) { return; } foreach (StabJoint item in (Il2CppArrayBase)(object)stabPoint.stabJoints) { if (item != null && !((Object)(object)item.collider == (Object)null)) { RigManager componentInParent = ((Component)item.collider).GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null) && TryGetPlayerId(componentInParent, out var playerId) && (!HitCooldowns.TryGetValue(playerId, out var value) || !(Time.unscaledTime < value))) { HitCooldowns[playerId] = Time.unscaledTime + 20f + 1f; SendSedation(playerId); break; } } } } private static bool IsFusionSyringe(Transform transform) { Transform val = transform; while ((Object)(object)val != (Object)null) { string name = ((Object)val).name; if (!string.IsNullOrEmpty(name) && (name.IndexOf("SyringeTest", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Tracker[SyringeTest]", StringComparison.OrdinalIgnoreCase) >= 0)) { return true; } val = val.parent; } return false; } private static bool TryGetPlayerId(RigManager rig, out byte playerId) { playerId = 0; if ((Object)(object)Player.RigManager != (Object)null && (Object)(object)rig == (Object)(object)Player.RigManager) { if (!NetworkInfo.HasServer) { return true; } playerId = PlayerIDManager.LocalSmallID; return true; } NetworkPlayer val = default(NetworkPlayer); if (!NetworkInfo.HasServer || !NetworkPlayerManager.TryGetPlayer(rig, ref val) || ((val != null) ? val.PlayerID : null) == null) { return false; } playerId = val.PlayerID.SmallID; return true; } private static void SendSedation(byte targetId) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown if (!NetworkInfo.HasServer) { SedateLocal(); } else if (targetId == PlayerIDManager.LocalSmallID) { SedateLocal(); } else if (!NativeMode) { MessageRelay.RelayModule(new SedateData { Target = targetId }, CommonMessageRoutes.ReliableToOtherClients); } else if (NetworkInfo.IsHost && _rulesSaved) { PlayerSender.SendPlayerDamage(targetId, new Attack { damage = 10000f }); Core instance = Instance; if (instance != null) { ((MelonBase)instance).LoggerInstance.Msg("Sent native Fusion knockout damage to player " + targetId); } } } private void TryRenameCrate() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown if (_crateRenamed || AssetWarehouse.Instance == null || !AssetWarehouse.ready) { return; } try { Crate val = default(Crate); if (AssetWarehouse.Instance.TryGetCrate(new Barcode("Bugrax.BLSyringe.Spawnable.SyringeTest"), ref val) && !((Object)(object)val == (Object)null)) { ((Scannable)val).Title = "Syringe (Fusion)"; ((Scannable)val).Description = "Stops a Fusion player for 20 seconds"; _crateRenamed = true; ((MelonBase)this).LoggerInstance.Msg("Spawn-menu item renamed to Syringe (Fusion)."); } } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Warning("Could not rename the syringe crate yet: " + ex.Message); } } } [HarmonyPatch(typeof(StabPoint), "SpawnStab")] internal static class SyringeStabPatch { private static void Postfix(StabPoint __instance, Transform tran) { Core.OnSyringeStab(__instance, tran); } } [HarmonyPatch(typeof(RemapRig), "ApplyMovement")] internal static class SedatedMovementPatch { private readonly struct MovementState { public readonly float MaxVelocity; public readonly float MaxAcceleration; public readonly float JumpVelocity; public readonly bool JumpEnabled; public MovementState(RemapRig rig) { MaxVelocity = rig.maxVelocity; MaxAcceleration = rig.maxAcceleration; JumpVelocity = rig.jumpVelocity; JumpEnabled = rig.jumpEnabled; } } private static void Prefix(RemapRig __instance, out MovementState? __state) { __state = null; if (Core.IsSedated && !((Object)(object)Player.RigManager == (Object)null) && !((Object)(object)Player.RigManager.remapHeptaRig != (Object)(object)__instance)) { __state = new MovementState(__instance); __instance.maxVelocity = 0f; __instance.maxAcceleration = 0f; __instance.jumpVelocity = 0f; __instance.jumpEnabled = false; } } private static void Finalizer(RemapRig __instance, MovementState? __state) { if (__state.HasValue) { MovementState value = __state.Value; __instance.maxVelocity = value.MaxVelocity; __instance.maxAcceleration = value.MaxAcceleration; __instance.jumpVelocity = value.JumpVelocity; __instance.jumpEnabled = value.JumpEnabled; } } } public sealed class SedateData : INetSerializable { public byte Target; public int? GetSize() { return 1; } public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref Target); } } public sealed class SedateMessage : ModuleMessageHandler { protected override void OnHandleMessage(ReceivedMessage message) { if (((ReceivedMessage)(ref message)).Sender.HasValue && PlayerIDManager.HasPlayerID(((ReceivedMessage)(ref message)).Sender.Value) && ((ReceivedMessage)(ref message)).ReadData().Target == PlayerIDManager.LocalSmallID) { Core.SedateLocal(); } } }