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 HarmonyLib; using Microsoft.CodeAnalysis; using PluginConfig.API; using PluginConfig.API.Fields; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("PiercerWallbang")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("PiercerWallbang")] [assembly: AssemblyTitle("PiercerWallbang")] [assembly: AssemblyVersion("1.0.0.0")] [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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PiercerWallbang { [BepInPlugin("codex.ultrakill.piercerwallbang", "Piercer Wallbang", "1.0.0")] [BepInProcess("ULTRAKILL.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string ModGuid = "codex.ultrakill.piercerwallbang"; public const string ModName = "Piercer Wallbang"; public const string ModVersion = "1.0.0"; internal static ConfigEntry Enabled; internal static ConfigEntry MaxDistance; internal static ConfigEntry StandardDamage; internal static ConfigEntry SlabDamage; internal static ConfigEntry Force; internal static ConfigEntry TraceRadius; internal static ConfigEntry HitEachEnemyOnce; internal static ConfigEntry RequireWallBeforeEnemy; internal static ConfigEntry DebugLogging; internal static ManualLogSource Log; private Harmony harmony; private PluginConfigurator pluginConfigurator; private BoolField enabledField; private void Awake() { //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enables the Piercer charged-shot wallbang behavior."); MaxDistance = ((BaseUnityPlugin)this).Config.Bind("General", "MaxDistance", 10000f, "Maximum wallbang ray distance."); StandardDamage = ((BaseUnityPlugin)this).Config.Bind("Damage", "StandardPiercerDamage", 3f, "Damage applied to enemies behind walls by the standard Piercer charged shot."); SlabDamage = ((BaseUnityPlugin)this).Config.Bind("Damage", "SlabPiercerDamage", 5f, "Damage applied to enemies behind walls by the Slab Piercer charged shot."); Force = ((BaseUnityPlugin)this).Config.Bind("Damage", "Force", 10000f, "Force applied in the shot direction when wallbang damage is delivered."); TraceRadius = ((BaseUnityPlugin)this).Config.Bind("General", "TraceRadius", 0.35f, "Radius around the charged shot ray used to catch enemy hitboxes behind walls."); HitEachEnemyOnce = ((BaseUnityPlugin)this).Config.Bind("Rules", "HitEachEnemyOnce", true, "Prevents multiple colliders on one enemy from causing duplicate damage from one wallbang shot."); RequireWallBeforeEnemy = ((BaseUnityPlugin)this).Config.Bind("Rules", "RequireWallBeforeEnemy", true, "When enabled, only enemies behind at least one blocking collider are damaged."); DebugLogging = ((BaseUnityPlugin)this).Config.Bind("Debug", "DebugLogging", false, "Logs wallbang ray hits to BepInEx/LogOutput.log."); harmony = new Harmony("codex.ultrakill.piercerwallbang"); harmony.PatchAll(); CreatePluginConfiguratorMenu(); Log.LogInfo((object)"Piercer Wallbang 1.0.0 loaded."); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private void CreatePluginConfiguratorMenu() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown try { pluginConfigurator = PluginConfigurator.Create("Piercer Wallbang", "codex.ultrakill.piercerwallbang"); pluginConfigurator.saveToFile = false; enabledField = new BoolField(pluginConfigurator.rootPanel, "Enabled", "enabled", Enabled.Value, false); enabledField.onValueChange += (BoolValueChangeEventDelegate)delegate(BoolValueChangeEvent data) { Enabled.Value = data.value; ((BaseUnityPlugin)this).Config.Save(); }; } catch (Exception ex) { Log.LogWarning((object)("PluginConfigurator menu was not created: " + ex.Message)); } } internal static bool IsPlayerOrWeaponCollider(Collider collider) { if ((Object)(object)collider == (Object)null) { return true; } if ((Object)(object)((Component)collider).GetComponentInParent() != (Object)null) { return true; } WeaponIdentifier componentInParent = ((Component)collider).GetComponentInParent(); return (Object)(object)componentInParent != (Object)null; } } [HarmonyPatch(typeof(Revolver), "Shoot")] internal static class RevolverShootPatch { private struct ShotState { public bool ShouldWallbang; public bool IsSlab; public float Charge; public bool PierceReady; public int ShotType; public GameObject SourceWeapon; } private static void Prefix(Revolver __instance, int shotType, out ShotState __state) { __state = default(ShotState); if (!Plugin.Enabled.Value || (Object)(object)__instance == (Object)null) { return; } bool value = Traverse.Create((object)__instance).Field("pierceReady").GetValue(); __state.ShouldWallbang = __instance.gunVariation == 0 && shotType == 2; __state.IsSlab = __instance.altVersion; __state.Charge = __instance.pierceShotCharge; __state.PierceReady = value; __state.ShotType = shotType; try { __state.SourceWeapon = MonoSingleton.Instance?.currentWeapon; } catch { __state.SourceWeapon = ((Component)__instance).gameObject; } } private static void Postfix(Revolver __instance, int shotType, ShotState __state) { if (!__state.ShouldWallbang) { if (Plugin.DebugLogging.Value && (Object)(object)__instance != (Object)null && __instance.gunVariation == 0) { Plugin.Log.LogInfo((object)$"Piercer shot ignored. shotType={shotType}, charge={__state.Charge:F2}, ready={__state.PierceReady}, slab={__state.IsSlab}"); } return; } if (Plugin.DebugLogging.Value) { Plugin.Log.LogInfo((object)$"Piercer wallbang trace started. shotType={__state.ShotType}, charge={__state.Charge:F2}, ready={__state.PierceReady}, slab={__state.IsSlab}"); } FireWallbang(__instance, __state); } private static void FireWallbang(Revolver revolver, ShotState state) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Unknown result type (might be due to invalid IL or missing references) Camera val = Traverse.Create((object)revolver).Field("cam").GetValue() ?? Camera.main; if ((Object)(object)val == (Object)null) { CameraController instance = MonoSingleton.Instance; val = (((Object)(object)instance != (Object)null && (Object)(object)instance.cam != (Object)null) ? instance.cam : null); } if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"Could not find a camera for Piercer wallbang ray."); return; } Vector3 position = ((Component)val).transform.position; Vector3 forward = ((Component)val).transform.forward; float num = Mathf.Max(1f, Plugin.MaxDistance.Value); float num2 = Mathf.Max(0f, Plugin.TraceRadius.Value); RaycastHit[] array = ((num2 > 0f) ? Physics.SphereCastAll(position, num2, forward, num, -1, (QueryTriggerInteraction)2) : Physics.RaycastAll(position, forward, num, -1, (QueryTriggerInteraction)2)); if (array == null || array.Length == 0) { if (Plugin.DebugLogging.Value) { Plugin.Log.LogInfo((object)"Piercer wallbang trace found no colliders."); } return; } Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance)); if (Plugin.DebugLogging.Value) { Plugin.Log.LogInfo((object)$"Piercer wallbang trace found {array.Length} colliders."); } float num3 = (state.IsSlab ? Plugin.SlabDamage.Value : Plugin.StandardDamage.Value); Vector3 val2 = forward * Plugin.Force.Value; HashSet hashSet = new HashSet(); bool flag = false; RaycastHit[] array2 = array; for (int i = 0; i < array2.Length; i++) { RaycastHit val3 = array2[i]; if ((Object)(object)((RaycastHit)(ref val3)).collider == (Object)null) { continue; } EnemyIdentifier enemy = GetEnemy(((RaycastHit)(ref val3)).collider); if ((Object)(object)enemy == (Object)null) { if (!Plugin.IsPlayerOrWeaponCollider(((RaycastHit)(ref val3)).collider) && !((RaycastHit)(ref val3)).collider.isTrigger) { flag = true; if (Plugin.DebugLogging.Value) { Plugin.Log.LogInfo((object)$"Wallbang blocker: {((Object)((RaycastHit)(ref val3)).collider).name} layer={((Component)((RaycastHit)(ref val3)).collider).gameObject.layer} distance={((RaycastHit)(ref val3)).distance:F2}"); } } } else { if ((Object)(object)enemy == (Object)null || enemy.dead) { continue; } if (Plugin.RequireWallBeforeEnemy.Value && !flag) { if (Plugin.DebugLogging.Value) { Plugin.Log.LogInfo((object)("Enemy " + ((Object)enemy).name + " is visible before any blocker; skipping duplicate wallbang damage.")); } } else if (!Plugin.HitEachEnemyOnce.Value || hashSet.Add(enemy)) { enemy.hitter = "piercer"; enemy.DeliverDamage(((Component)((RaycastHit)(ref val3)).collider).gameObject, val2, ((RaycastHit)(ref val3)).point, num3, false, 0f, state.SourceWeapon, false, false); if (Plugin.DebugLogging.Value) { Plugin.Log.LogInfo((object)$"Wallbang hit {((Object)enemy).name} at {((RaycastHit)(ref val3)).distance:F2}m for {num3:F2} damage. shotType={state.ShotType}, charge={state.Charge:F2}, ready={state.PierceReady}"); } } } } } private static EnemyIdentifier GetEnemy(Collider collider) { EnemyIdentifierIdentifier componentInParent = ((Component)collider).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.eid != (Object)null) { return componentInParent.eid; } return ((Component)collider).GetComponentInParent(); } } }