Please disclose if your mod was created primarily 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 LC Lives v1.0.3
slenered.LC_Lives.dll
Decompiled a year agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CSync.Extensions; using CSync.Lib; using GameNetcodeStuff; using HarmonyLib; using LCVR; using LCVR.Patches.Spectating; using LCVR.Player; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("LCVR")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("slenered.LC_Lives")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Adds lives to Lethal Company for parties less than for.")] [assembly: AssemblyFileVersion("1.0.3.0")] [assembly: AssemblyInformationalVersion("1.0.3")] [assembly: AssemblyProduct("slenered.LC_Lives")] [assembly: AssemblyTitle("slenered.LC_Lives")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.3.0")] [module: UnverifiableCode] [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.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] [Microsoft.CodeAnalysis.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] [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 LC_Lives { public class Configs : SyncedConfig2<Configs> { [field: SyncedEntryField] public SyncedEntry<int> PartySize { get; private set; } [field: SyncedEntryField] public SyncedEntry<int> GlobalLives { get; private set; } [field: SyncedEntryField] public SyncedEntry<int> PlayerLives { get; private set; } [field: SyncedEntryField] public SyncedEntry<float> RespawnTimeSeconds { get; private set; } [field: SyncedEntryField] public SyncedEntry<bool> ReviveOnBodyCollectedBool { get; private set; } [field: SyncedEntryField] public SyncedEntry<bool> PreventShipFromLeaving { get; private set; } public Configs(ConfigFile configFile) : base("slenered.LC_Lives") { PartySize = SyncedBindingExtensions.BindSyncedEntry<int>(configFile, "General", "Party Size", 4, "The number lives that the team shares. (Each player consumes a life by landing)"); GlobalLives = SyncedBindingExtensions.BindSyncedEntry<int>(configFile, "General", "Global Lives", 0, "Additional lives that the team shares."); PlayerLives = SyncedBindingExtensions.BindSyncedEntry<int>(configFile, "General", "Player Lives", 0, "The number lives that each player has."); ReviveOnBodyCollectedBool = SyncedBindingExtensions.BindSyncedEntry<bool>(configFile, "General", "Revive on body collection", true, "Force revive a player when their body is returned to the ship."); RespawnTimeSeconds = SyncedBindingExtensions.BindSyncedEntry<float>(configFile, "General", "Respawn Timer (Seconds)", 30f, "The amount of time before respawning a player."); PreventShipFromLeaving = SyncedBindingExtensions.BindSyncedEntry<bool>(configFile, "General", "Prevent Ship From Leaving", false, "Stop the ship from leaving if there are lives left."); ConfigManager.Register<Configs>((SyncedConfig2<Configs>)(object)this); PartySize.Changed += delegate(object _, SyncedSettingChangedEventArgs<int> args) { Logger.LogInfo((object)$"(NOW) PartySize: {args.OldValue} -> {args.NewValue}"); }; GlobalLives.Changed += delegate(object _, SyncedSettingChangedEventArgs<int> args) { Logger.LogInfo((object)$"(NOW) GlobalLives: {args.OldValue} -> {args.NewValue}"); }; PlayerLives.Changed += delegate(object _, SyncedSettingChangedEventArgs<int> args) { Logger.LogInfo((object)$"(NOW) PlayerLives: {args.OldValue} -> {args.NewValue}"); }; PreventShipFromLeaving.Changed += delegate(object _, SyncedSettingChangedEventArgs<bool> args) { Logger.LogInfo((object)$"(NOW) PreventShipFromLeaving: {args.OldValue} -> {args.NewValue}"); }; RespawnTimeSeconds.Changed += delegate(object _, SyncedSettingChangedEventArgs<float> args) { if (args.NewValue > 0f) { Logger.LogInfo((object)"(NOW) On Timer!"); LC_Lives.RevivePlayerMetric.RevivePlayerSystem.AddReviveCondition(new LC_Lives.RevivePlayerMetric.ReviveOnTimer()); } else { Logger.LogInfo((object)"(NOW) Off on Timer!"); LC_Lives.RevivePlayerMetric.RevivePlayerSystem.RemoveReviveCondition(new LC_Lives.RevivePlayerMetric.ReviveOnTimer()); } }; ReviveOnBodyCollectedBool.Changed += delegate(object _, SyncedSettingChangedEventArgs<bool> args) { if (args.NewValue) { Logger.LogInfo((object)"(NOW) On Body Collect!"); LC_Lives.RevivePlayerMetric.RevivePlayerSystem.AddReviveCondition(new LC_Lives.RevivePlayerMetric.ReviveOnBodyCollected()); } else { Logger.LogInfo((object)"(NOW) Off on Body Collect!"); LC_Lives.RevivePlayerMetric.RevivePlayerSystem.RemoveReviveCondition(new LC_Lives.RevivePlayerMetric.ReviveOnBodyCollected()); } }; } } [BepInPlugin("slenered.LC_Lives", "slenered.LC_Lives", "1.0.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency("com.sigurd.csync", "5.0.1")] public class LC_Lives : BaseUnityPlugin { internal class RevivePlayerMetric { internal static class RevivePlayerSystem { private static readonly List<IReviveCondition> SReviveActions = new List<IReviveCondition>(); private static readonly Dictionary<ulong, RevivePlayerMetric> SPlayerMetrics = new Dictionary<ulong, RevivePlayerMetric>(); public static void AddReviveCondition(IReviveCondition condition) { SReviveActions.Add(condition); } public static void RemoveReviveCondition(IReviveCondition condition) { SReviveActions.Remove(condition); } public static void ClearReviveActions() { SReviveActions.Clear(); } [HarmonyPatch(typeof(StartOfRound), "ShipLeaveAutomatically")] [HarmonyPrefix] private static bool ShipLeaveAutomaticallyPatch(StartOfRound __instance) { bool flag = false; foreach (RevivePlayerMetric value in SPlayerMetrics.Values) { if (value.CanRevive()) { flag = true; break; } } Logger.LogInfo((object)TimeOfDay.Instance.shipLeavingOnMidnight); if (_config.PreventShipFromLeaving.Value && flag && !TimeOfDay.Instance.shipLeavingOnMidnight) { __instance.allPlayersDead = false; return false; } return true; } [HarmonyPatch(typeof(RoundManager), "FinishGeneratingNewLevelClientRpc")] [HarmonyPrefix] private static void ResetMetricsOnRoundStart() { Logger.LogError((object)"Round Manager Finished Level Generation!"); StartOfRound instance = StartOfRound.Instance; SPlayerMetrics.Clear(); if (instance == null) { Logger.LogError((object)"Start of Round is null at 'FinishGeneratingNewLevelClientRpc'"); return; } SReviveActions.ForEach(delegate(IReviveCondition x) { x.Reset(); }); int value = _config.PlayerLives.Value; PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; PlayerControllerB[] array = allPlayerScripts; foreach (PlayerControllerB val in array) { ulong actualClientId = val.actualClientId; string playerUsername = val.playerUsername; if (actualClientId == 0L && !val.isHostPlayerObject) { break; } Logger.LogInfo((object)$"Starting Revive Tracking for: '{actualClientId}' => '{playerUsername}'"); SPlayerMetrics.Add(val.actualClientId, new RevivePlayerMetric(val, value)); } Instance.GlobalLivesLeft = _config.GlobalLives.Value + Math.Max(_config.PartySize.Value - instance.livingPlayers, 0); if (Object.op_Implicit((Object)(object)HUDManager.Instance)) { HUDManager.Instance.DisplayTip("Lives", $"{Instance.GlobalLivesLeft} Global lives,\n{value} Personal lives.", false, false, "LC_Tip1"); } } [HarmonyPatch(typeof(StartOfRound), "Update")] [HarmonyPrefix] private static void TryRevivePlayers(ref StartOfRound __instance) { //IL_0134: Unknown result type (might be due to invalid IL or missing references) if (!__instance.shipHasLanded || __instance.inShipPhase) { return; } if (SPlayerMetrics.Count <= 0 || SReviveActions.Count <= 0) { Logger.LogInfo((object)"No revive actions or players available to revive"); return; } foreach (KeyValuePair<ulong, RevivePlayerMetric> sPlayerMetric in SPlayerMetrics) { sPlayerMetric.Deconstruct(out var key, out var value); ulong num = key; RevivePlayerMetric revivePlayerMetric = value; PlayerControllerB player = revivePlayerMetric.Player; if (!player.isPlayerDead || (player != null && player.actualClientId == 0L && !player.isHostPlayerObject)) { continue; } foreach (IReviveCondition sReviveAction in SReviveActions) { if (revivePlayerMetric.CanRevive() && sReviveAction.ShouldRevivePlayer(revivePlayerMetric)) { SReviveActions.ForEach(delegate(IReviveCondition x) { x.SoftReset(revivePlayerMetric); }); Logger.LogInfo((object)$"Reviving Player '{num}' => '{player.playerUsername}'"); revivePlayerMetric.Revive(sReviveAction.GetRevivePosition(revivePlayerMetric)); } } } } } internal class ReviveOnBodyCollected : IReviveCondition { private readonly Dictionary<ulong, float> _mTimers = new Dictionary<ulong, float>(); public void Reset() { _mTimers.Clear(); } public void SoftReset(RevivePlayerMetric metric) { ulong actualClientId = metric.Player.actualClientId; _mTimers[actualClientId] = 0f; } public bool ShouldRevivePlayer(RevivePlayerMetric metric) { if (_config.ReviveOnBodyCollectedBool.Value && metric.Player.isPlayerDead) { ulong actualClientId = metric.Player.actualClientId; float num = 5f; if (!_mTimers.TryGetValue(actualClientId, out var value)) { _mTimers.Add(actualClientId, 0f); return false; } _mTimers[actualClientId] = value + Time.deltaTime; value = _mTimers[actualClientId]; if (value > num) { _mTimers[actualClientId] = 0f; return metric.Player.deadBody.isInShip; } } return false; } } internal class ReviveOnTimer : IReviveCondition { private readonly Dictionary<ulong, float> _mTimers = new Dictionary<ulong, float>(); public void Reset() { _mTimers.Clear(); } public void SoftReset(RevivePlayerMetric metric) { ulong actualClientId = metric.Player.actualClientId; _mTimers[actualClientId] = 0f; } public bool ShouldRevivePlayer(RevivePlayerMetric metric) { if (!metric.Player.isPlayerDead) { return false; } ulong actualClientId = metric.Player.actualClientId; float value = _config.RespawnTimeSeconds.Value; if (!_mTimers.TryGetValue(actualClientId, out var value2)) { _mTimers.Add(actualClientId, 0f); return false; } _mTimers[actualClientId] = value2 + Time.deltaTime; value2 = _mTimers[actualClientId]; if (value2 > value) { _mTimers[actualClientId] = 0f; return true; } return false; } } private PlayerControllerB Player { get; } private int LivesLeft { get; set; } private RevivePlayerMetric(PlayerControllerB player, int revives) { Player = player; LivesLeft = revives; } private bool CanRevive() { return LivesLeft + Instance.GlobalLivesLeft > 0; } private int GetPlayerIndex() { StartOfRound instance = StartOfRound.Instance; if (instance == null) { throw new NullReferenceException("Start of round is null"); } for (int i = 0; i < instance.allPlayerScripts.Length; i++) { PlayerControllerB val = instance.allPlayerScripts[i]; if (val.actualClientId == Player.actualClientId && (val.actualClientId != 0L || val.isHostPlayerObject)) { return i; } } throw new ArgumentException("Unknown Player Index: " + (object)Player); } private void Revive(Vector3 revivePos) { //IL_0114: 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) if (!CanRevive()) { return; } StartOfRound instance = StartOfRound.Instance; if (Instance.inVR) { ReviveVRPlayer(); } int playerIndex = GetPlayerIndex(); instance.livingPlayers++; Player.isClimbingLadder = false; Player.clampLooking = false; Player.inVehicleAnimation = false; Player.disableMoveInput = false; Player.ResetZAndXRotation(); ((Collider)Player.thisController).enabled = true; Player.health = 100; Player.hasBeenCriticallyInjured = false; Player.disableLookInput = false; Player.disableInteract = false; Player.isPlayerDead = false; Player.isPlayerControlled = true; Player.isInElevator = true; Player.isInHangarShipRoom = true; Player.isInsideFactory = false; Player.parentedToElevatorLastFrame = false; Player.overrideGameOverSpectatePivot = null; instance.SetPlayerObjectExtrapolate(false); Player.TeleportPlayer(revivePos, false, 0f, false, true); Player.setPositionOfDeadPlayer = false; Player.DisablePlayerModel(instance.allPlayerObjects[playerIndex], true, true); ((Behaviour)Player.helmetLight).enabled = false; Player.Crouch(false); Player.criticallyInjured = false; if ((Object)(object)Player.playerBodyAnimator != (Object)null) { Player.playerBodyAnimator.SetBool(LimpAnimator, false); } Player.bleedingHeavily = false; Player.activatingItem = false; Player.twoHanded = false; Player.inShockingMinigame = false; Player.inSpecialInteractAnimation = false; Player.freeRotationInInteractAnimation = false; Player.disableSyncInAnimation = false; Player.inAnimationWithEnemy = null; Player.holdingWalkieTalkie = false; Player.speakingToWalkieTalkie = false; Player.isSinking = false; Player.isUnderwater = false; Player.sinkingValue = 0f; Player.statusEffectAudio.Stop(); Player.DisableJetpackControlsLocally(); Player.mapRadarDotAnimator.SetBool(DeadAnimator, false); Player.externalForceAutoFade = Vector3.zero; if (((NetworkBehaviour)Player).IsOwner) { HUDManager.Instance.gasHelmetAnimator.SetBool(GasEmittingAnimator, false); Player.hasBegunSpectating = false; HUDManager.Instance.RemoveSpectateUI(); HUDManager.Instance.gameOverAnimator.SetTrigger(ReviveAnimator); Player.hinderedMultiplier = 1f; Player.isMovementHindered = 0; Player.sourcesCausingSinking = 0; Player.reverbPreset = instance.shipReverb; HUDManager.Instance.UpdateHealthUI(100, false); ((Behaviour)HUDManager.Instance.audioListenerLowPass).enabled = false; HUDManager.Instance.HideHUD(false); } SoundManager.Instance.earsRingingTimer = 0f; Player.voiceMuffledByEnemy = false; SoundManager.Instance.playerVoicePitchTargets[playerIndex] = 1f; SoundManager.Instance.SetPlayerPitch(1f, playerIndex); if ((Object)(object)Player.currentVoiceChatIngameSettings == (Object)null) { instance.RefreshPlayerVoicePlaybackObjects(); } if ((Object)(object)Player.currentVoiceChatIngameSettings != (Object)null) { if ((Object)(object)Player.currentVoiceChatIngameSettings.voiceAudio == (Object)null) { Player.currentVoiceChatIngameSettings.InitializeComponents(); } if ((Object)(object)Player.currentVoiceChatIngameSettings.voiceAudio == (Object)null) { return; } ((Component)Player.currentVoiceChatIngameSettings.voiceAudio).GetComponent<OccludeAudio>().overridingLowPass = false; } Player.spectatedPlayerScript = null; instance.SetSpectateCameraToGameOverMode(false, Player); instance.UpdatePlayerVoiceEffects(); Logger.LogInfo((object)$"LivesLeft: {LivesLeft} | GlobalLivesLeft: {Instance.GlobalLivesLeft}"); if (LivesLeft > 0) { int livesLeft = LivesLeft - 1; LivesLeft = livesLeft; if (Object.op_Implicit((Object)(object)HUDManager.Instance) && ((NetworkBehaviour)Player).IsOwner) { HUDManager.Instance.DisplayTip("Lives", $"You have {LivesLeft} live(s) left.", LivesLeft <= 1, false, "LC_Tip1"); } } else { int globalLivesLeft = Instance.GlobalLivesLeft - 1; Instance.GlobalLivesLeft = globalLivesLeft; if (Object.op_Implicit((Object)(object)HUDManager.Instance)) { HUDManager.Instance.DisplayTip("Lives", $"There are {Instance.GlobalLivesLeft} team live(s) left.", Instance.GlobalLivesLeft <= 1, false, "LC_Tip1"); } } } private void ReviveVRPlayer() { SpectatorPlayerPatches.OnPlayerRevived(); } } internal interface IReviveCondition { void Reset(); void SoftReset(RevivePlayerMetric metric); bool ShouldRevivePlayer(RevivePlayerMetric metric); Vector3 GetRevivePosition(RevivePlayerMetric _) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) return StartOfRound.Instance.middleOfShipNode.position; } } public bool inVR; private static Configs _config = null; private static readonly int LimpAnimator = Animator.StringToHash("Limp"); private static readonly int DeadAnimator = Animator.StringToHash("dead"); private static readonly int GasEmittingAnimator = Animator.StringToHash("gasEmitting"); private static readonly int ReviveAnimator = Animator.StringToHash("revive"); private static LC_Lives Instance { get; set; } = null; private static ManualLogSource Logger { get; set; } = null; private static Harmony? Harmony { get; set; } private int GlobalLivesLeft { get; set; } private void Awake() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown Logger = ((BaseUnityPlugin)this).Logger; Instance = this; Harmony = new Harmony("slenered.LC_Lives"); if (Chainloader.PluginInfos.ContainsKey("io.daxcess.lcvr")) { LCVRCompatibility(); } else { Logger.LogInfo((object)"LCVR was not found. Skipping..."); } _config = new Configs(((BaseUnityPlugin)this).Config); ((SyncedConfig2<Configs>)(object)_config).InitialSyncCompleted += delegate { Logger.LogInfo((object)"Initial sync complete!"); if (_config.ReviveOnBodyCollectedBool.Value) { Logger.LogInfo((object)"On Body Collect!"); RevivePlayerMetric.RevivePlayerSystem.AddReviveCondition(new RevivePlayerMetric.ReviveOnBodyCollected()); } if (_config.RespawnTimeSeconds.Value > 0f) { Logger.LogInfo((object)"On Timer!"); RevivePlayerMetric.RevivePlayerSystem.AddReviveCondition(new RevivePlayerMetric.ReviveOnTimer()); } }; try { Harmony.PatchAll(typeof(RevivePlayerMetric.RevivePlayerSystem)); } catch (Exception ex) { Logger.LogError((object)("Failed to patch Revive System; '" + ex.Message + "'\n" + ex.StackTrace)); } Logger.LogInfo((object)$"PartySize: {_config.PartySize.Value}"); Logger.LogInfo((object)$"GlobalLives: {_config.GlobalLives.Value}"); Logger.LogInfo((object)$"PlayerLives: {_config.PlayerLives.Value}"); Logger.LogInfo((object)$"ReviveOnBodyCollectedBool: {_config.ReviveOnBodyCollectedBool.Value}"); Logger.LogInfo((object)$"RespawnTimeSeconds: {_config.RespawnTimeSeconds.Value}"); Logger.LogInfo((object)$"PreventShipFromLeaving: {_config.PreventShipFromLeaving.Value}"); Logger.LogInfo((object)"------------------------------------------"); Logger.LogInfo((object)"slenered.LC_Lives v1.0.3 has loaded!"); } private void LCVRCompatibility() { Logger.LogInfo((object)"LCVR Found!"); inVR = VRSession.InVR; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "slenered.LC_Lives"; public const string PLUGIN_NAME = "slenered.LC_Lives"; public const string PLUGIN_VERSION = "1.0.3"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }