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 SoulboundNoKill v0.1.0
SoulboundNoKill.dll
Decompiled 19 hours 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 HarmonyLib; using RoR2; using UnityEngine; using UnityEngine.Networking; [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("SoulboundNoKill")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SoulboundNoKill")] [assembly: AssemblyTitle("SoulboundNoKill")] [assembly: AssemblyVersion("1.0.0.0")] namespace SoulboundNoKill; [BepInPlugin("tesv0.SoulboundNoKill", "SoulboundNoKill", "0.1.0")] public class SoulboundNoKillPlugin : BaseUnityPlugin { public const string Guid = "tesv0.SoulboundNoKill"; public const string Name = "SoulboundNoKill"; public const string Version = "0.1.0"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<string> SceneMatch; internal static ConfigEntry<float> FallDepth; internal static ConfigEntry<float> InvulnSeconds; internal static ConfigEntry<bool> InterceptMapZones; internal static ConfigEntry<bool> InterceptSuicide; internal static ConfigEntry<bool> InterceptEnvironmentDamage; internal static ConfigEntry<bool> InterceptTrueKill; internal static ConfigEntry<bool> DebugLogging; private static readonly Dictionary<CharacterBody, Vector3> lastSafePosition = new Dictionary<CharacterBody, Vector3>(); private static readonly Dictionary<CharacterBody, float> lastRescueTime = new Dictionary<CharacterBody, float>(); private static bool inTargetScene; private static bool Active { get { if (Enabled.Value && inTargetScene) { return NetworkServer.active; } return false; } } private void Awake() { //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Expected O, but got Unknown //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Expected O, but got Unknown //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown //IL_01a8: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the mod on or off."); SceneMatch = ((BaseUnityPlugin)this).Config.Bind<string>("General", "SceneMatch", "soulbound", "Comma-separated, case-insensitive substrings. The mod is active on a stage whose internal name, name token or English name contains any of them. The stage name is written to the log on every stage load."); FallDepth = ((BaseUnityPlugin)this).Config.Bind<float>("Detection", "FallDepth", 8f, "A death only counts as a pit death if the player is this many meters below the last ground they stood on. Protects normal deaths (enemies, timers) from being cancelled."); InvulnSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Rescue", "InvulnerabilitySeconds", 2f, "Seconds of invulnerability after being put back on the ground."); InterceptMapZones = ((BaseUnityPlugin)this).Config.Bind<bool>("Detection", "InterceptKillZones", true, "Intercept map trigger zones of a type other than the vanilla OutOfBounds/KickOutPlayers (a new kill zone type) and treat them as out of bounds."); InterceptSuicide = ((BaseUnityPlugin)this).Config.Bind<bool>("Detection", "InterceptSuicide", true, "Intercept HealthComponent.Suicide on a falling player."); InterceptEnvironmentDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("Detection", "InterceptEnvironmentDamage", true, "Intercept lethal damage that does not come from an enemy (no attacker body) on a falling player."); InterceptTrueKill = ((BaseUnityPlugin)this).Config.Bind<bool>("Detection", "InterceptTrueKill", true, "Intercept CharacterMaster.TrueKill on a falling player."); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseLogging", true, "Log every player death in the target stage with its details. Send this log to the mod author if the pit still kills you."); Harmony val = new Harmony("tesv0.SoulboundNoKill"); TryPatch(val, typeof(MapZone), "TryZoneStart", "MapZoneTryZoneStartPrefix"); TryPatch(val, typeof(HealthComponent), "Suicide", "SuicidePrefix"); TryPatch(val, typeof(HealthComponent), "TakeDamage", "TakeDamagePrefix"); TryPatch(val, typeof(CharacterMaster), "TrueKill", "TrueKillPrefix"); SceneCatalog.onMostRecentSceneDefChanged += OnSceneChanged; } private static void TryPatch(Harmony harmony, Type type, string methodName, string prefixName) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown HarmonyMethod val = new HarmonyMethod(typeof(SoulboundNoKillPlugin).GetMethod(prefixName, BindingFlags.Static | BindingFlags.NonPublic)); int num = 0; MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != methodName)) { try { harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; } catch (Exception ex) { Log.LogWarning((object)("Could not patch " + type.Name + "." + methodName + ": " + ex.Message)); } } } if (num == 0) { Log.LogWarning((object)(type.Name + "." + methodName + " not found; that detection path is off.")); } } private static void OnSceneChanged(SceneDef sceneDef) { lastSafePosition.Clear(); lastRescueTime.Clear(); inTargetScene = false; if (!Object.op_Implicit((Object)(object)sceneDef)) { return; } string text = ""; try { text = Language.GetString(sceneDef.nameToken, "en") ?? ""; } catch { } string text2 = (sceneDef.cachedName + "|" + sceneDef.nameToken + "|" + text).ToLowerInvariant(); string[] array = SceneMatch.Value.Split(','); for (int i = 0; i < array.Length; i++) { string text3 = array[i].Trim().ToLowerInvariant(); if (text3.Length > 0 && text2.Contains(text3)) { inTargetScene = true; } } Log.LogInfo((object)("Stage loaded: name='" + sceneDef.cachedName + "' token='" + sceneDef.nameToken + "' english='" + text + "' -> mod " + (inTargetScene ? "ACTIVE" : "idle"))); } private void FixedUpdate() { //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (!Active) { return; } foreach (PlayerCharacterMasterController instance in PlayerCharacterMasterController.instances) { CharacterBody val = ((Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)instance.master)) ? instance.master.GetBody() : null); if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.characterMotor) && val.characterMotor.isGrounded) { lastSafePosition[val] = val.footPosition; } } } private static bool IsPlayer(CharacterBody body) { if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.master)) { return Object.op_Implicit((Object)(object)body.master.playerCharacterMasterController); } return false; } private static bool IsFallingIntoPit(CharacterBody body) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)body.characterMotor) && body.characterMotor.isGrounded) { return false; } if (!lastSafePosition.TryGetValue(body, out var value)) { return false; } return body.footPosition.y < value.y - FallDepth.Value; } private static void LogDeath(string source, CharacterBody body, string details) { //IL_0057: 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) if (DebugLogging.Value) { lastSafePosition.TryGetValue(body, out var value); bool flag = Object.op_Implicit((Object)(object)body.characterMotor) && body.characterMotor.isGrounded; Log.LogInfo((object)$"[{source}] player={body.GetDisplayName()} pos={body.footPosition} lastGround={value} grounded={flag} {details}"); } } private static bool Rescue(CharacterBody body, string source) { //IL_003f: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) if (lastRescueTime.TryGetValue(body, out var value) && Time.time - value < 0.5f) { return true; } lastRescueTime[body] = Time.time; if (!lastSafePosition.TryGetValue(body, out var value2)) { Vector3? val = TeleportHelper.FindSafeTeleportDestination(body.footPosition, body, RoR2Application.rng); if (!val.HasValue) { Log.LogWarning((object)("[" + source + "] No safe ground found for " + body.GetDisplayName() + "; letting the death happen.")); return false; } value2 = val.Value; } if (Object.op_Implicit((Object)(object)body.characterMotor)) { body.characterMotor.velocity = Vector3.zero; } TeleportHelper.TeleportBody(body, value2); if (InvulnSeconds.Value > 0f) { body.AddTimedBuff(Buffs.HiddenInvincibility, InvulnSeconds.Value); } Log.LogInfo((object)$"[{source}] Saved {body.GetDisplayName()} from the pit, moved to {value2}."); return true; } private static bool MapZoneTryZoneStartPrefix(MapZone __instance, Collider other) { if (!Active || !InterceptMapZones.Value || !Object.op_Implicit((Object)(object)other)) { return true; } string text = ((object)Unsafe.As<ZoneType, ZoneType>(ref __instance.zoneType)/*cast due to .constrained prefix*/).ToString(); if (text == "OutOfBounds" || text == "KickOutPlayers") { return true; } CharacterBody component = ((Component)other).GetComponent<CharacterBody>(); if (!IsPlayer(component)) { return true; } LogDeath("MapZone", component, "zoneType=" + text + " zone='" + ((Object)__instance).name + "'"); return !Rescue(component, "MapZone"); } private static bool SuicidePrefix(HealthComponent __instance) { if (!Active || !InterceptSuicide.Value) { return true; } CharacterBody body = __instance.body; if (!IsPlayer(body)) { return true; } LogDeath("Suicide", body, ""); if (!IsFallingIntoPit(body)) { return true; } return !Rescue(body, "Suicide"); } private static bool TakeDamagePrefix(HealthComponent __instance, DamageInfo damageInfo) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) if (!Active || !InterceptEnvironmentDamage.Value || damageInfo == null) { return true; } CharacterBody body = __instance.body; if (!IsPlayer(body) || !__instance.alive) { return true; } if (damageInfo.damage < __instance.combinedHealth) { return true; } CharacterBody obj = (Object.op_Implicit((Object)(object)damageInfo.attacker) ? damageInfo.attacker.GetComponent<CharacterBody>() : null); LogDeath("TakeDamage", body, string.Format("damage={0} type={1} attacker='{2}' ", damageInfo.damage, damageInfo.damageType, Object.op_Implicit((Object)(object)damageInfo.attacker) ? ((Object)damageInfo.attacker).name : "none") + "inflictor='" + (Object.op_Implicit((Object)(object)damageInfo.inflictor) ? ((Object)damageInfo.inflictor).name : "none") + "'"); if (Object.op_Implicit((Object)(object)obj)) { return true; } if (!IsFallingIntoPit(body)) { return true; } return !Rescue(body, "TakeDamage"); } private static bool TrueKillPrefix(CharacterMaster __instance) { if (!Active || !InterceptTrueKill.Value) { return true; } CharacterBody body = __instance.GetBody(); if (!IsPlayer(body)) { return true; } LogDeath("TrueKill", body, ""); if (!IsFallingIntoPit(body)) { return true; } return !Rescue(body, "TrueKill"); } }