Decompiled source of EasyCampaignTweaks v1.1.0
EasyCampaignTweaks.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; 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 TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [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("EasyCampaignTweaks")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("EasyCampaignTweaks")] [assembly: AssemblyTitle("EasyCampaignTweaks")] [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 EasyCampaignTweaks { [BepInPlugin("twistedscarlett.ultrakill.easycampaigntweaks", "Easy Campaign Tweaks", "1.1.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class EasyCampaignTweaksPlugin : BaseUnityPlugin { private sealed class HealthBarManager : MonoBehaviour { private EasyCampaignTweaksPlugin _owner; private Canvas _canvas; private RectTransform _canvasRect; private TMP_FontAsset _font; private Sprite _whiteSprite; private readonly Dictionary<int, HealthBarEntry> _bars = new Dictionary<int, HealthBarEntry>(); private readonly List<int> _removeBuffer = new List<int>(); private float _nextScanTime; private static readonly Color OuterBorder = new Color(0.02f, 0.02f, 0.025f, 0.96f); private static readonly Color InnerBorder = new Color(0.78f, 0.79f, 0.84f, 0.92f); private static readonly Color EmptyGrey = new Color(0.36f, 0.36f, 0.38f, 0.92f); private static readonly Color DamageRed = new Color(0.86f, 0.05f, 0.04f, 0.96f); private static readonly Color NormalOrange = new Color(1f, 0.78f, 0.2f, 1f); private static readonly Color BossRed = new Color(1f, 0.24f, 0.18f, 1f); private static readonly Color BlessedBlue = new Color(0.3f, 0.85f, 1f, 1f); private const float HealthbarLengthExponent = 0.55f; private const float HealthbarWidthBase = 12.5f; public void Initialize(EasyCampaignTweaksPlugin owner) { _owner = owner; _nextScanTime = Time.time + 0.5f; CreateCanvas(); } private void OnDestroy() { ClearBars(); if ((Object)(object)_canvas != (Object)null) { Object.Destroy((Object)(object)((Component)_canvas).gameObject); } if ((Object)(object)_whiteSprite != (Object)null) { Object.Destroy((Object)(object)_whiteSprite.texture); } } private void Update() { if ((Object)(object)_owner == (Object)null || _owner._healthBarsEnabled == null || !_owner._healthBarsEnabled.Value) { ClearBars(); if ((Object)(object)_canvas != (Object)null) { ((Component)_canvas).gameObject.SetActive(false); } return; } if ((Object)(object)_canvas == (Object)null) { CreateCanvas(); } if (!((Object)(object)_canvas == (Object)null)) { if (!((Component)_canvas).gameObject.activeSelf) { ((Component)_canvas).gameObject.SetActive(true); } if (Time.time >= _nextScanTime) { _nextScanTime = Time.time + 1.25f; ScanExistingEnemies(); } UpdateBars(); } } private void CreateCanvas() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown if (!((Object)(object)_canvas != (Object)null)) { GameObject val = new GameObject("Easy Campaign Tweaks Health Bars", new Type[3] { typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; _canvas = val.GetComponent<Canvas>(); _canvas.renderMode = (RenderMode)0; _canvas.sortingOrder = 32000; CanvasScaler component = val.GetComponent<CanvasScaler>(); component.uiScaleMode = (ScaleMode)0; component.scaleFactor = 1f; ref RectTransform canvasRect = ref _canvasRect; Transform transform = val.transform; canvasRect = (RectTransform)(object)((transform is RectTransform) ? transform : null); _font = FindUltrakillFont(); _whiteSprite = CreateWhiteSprite(); } } private Sprite CreateWhiteSprite() { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false); val.SetPixel(0, 0, Color.white); val.Apply(false, true); ((Object)val).hideFlags = (HideFlags)61; Sprite obj = Sprite.Create(val, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 1f); ((Object)obj).hideFlags = (HideFlags)61; return obj; } private TMP_FontAsset FindUltrakillFont() { try { TMP_FontAsset val = null; TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset; TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); foreach (TMP_FontAsset val2 in array) { if (!((Object)(object)val2 == (Object)null) && !string.IsNullOrEmpty(((Object)val2).name)) { string text = Normalize(((Object)val2).name); if (text.Contains("vcr") || text.Contains("terminal") || text.Contains("hud") || text.Contains("ultrakill") || text.Contains("fontmain")) { return val2; } if ((Object)(object)val == (Object)null && (text.Contains("digital") || text.Contains("mono") || text.Contains("pixel"))) { val = val2; } } } return ((Object)(object)val != (Object)null) ? val : defaultFontAsset; } catch { return TMP_Settings.defaultFontAsset; } } private void ScanExistingEnemies() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) Type enemyIdentifierType = _owner.GetEnemyIdentifierType(); if (enemyIdentifierType == null) { return; } try { Object[] array = Resources.FindObjectsOfTypeAll(enemyIdentifierType); foreach (Object obj in array) { Component val = (Component)(object)((obj is Component) ? obj : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.gameObject == (Object)null) && val.gameObject.activeInHierarchy) { Scene scene = val.gameObject.scene; if (((Scene)(ref scene)).IsValid()) { Track(val, val.gameObject); } } } } catch { } } public void Track(Component enemyIdentifier, GameObject source) { if ((Object)(object)_owner == (Object)null || (Object)(object)enemyIdentifier == (Object)null || (Object)(object)source == (Object)null || _owner._healthBarsEnabled == null || !_owner._healthBarsEnabled.Value || !source.activeInHierarchy) { return; } int instanceID = ((Object)source).GetInstanceID(); if (_bars.ContainsKey(instanceID)) { return; } Type type = ((object)enemyIdentifier).GetType(); bool flag = _owner.ReadBoolMember(type, enemyIdentifier, "isBoss", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (flag && !_owner._healthBarsShowBosses.Value) { return; } EnemyProfile healthBarProfile = _owner.GetHealthBarProfile(enemyIdentifier, source); float num = ReadDisplayHealth(type, enemyIdentifier, source, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, healthBarProfile); if (num <= 0f) { return; } float num2 = ResolveDisplayMaxHealth(type, enemyIdentifier, source, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, healthBarProfile, num); if (!(num2 < Mathf.Max(0f, _owner._healthBarsMinHealth.Value))) { if ((Object)(object)_canvas == (Object)null) { CreateCanvas(); } float healthModifier = Mathf.Max(0.01f, _owner.ReadFloatMember(type, enemyIdentifier, "totalHealthModifier", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, 1f)); string healthBarDisplayNameFromProfile = _owner.GetHealthBarDisplayNameFromProfile(healthBarProfile, enemyIdentifier, source); HealthBarEntry healthBarEntry = new HealthBarEntry(source, enemyIdentifier, num2, healthModifier, healthBarDisplayNameFromProfile, flag, healthBarProfile.Key); CreateEntryUi(healthBarEntry); _bars[instanceID] = healthBarEntry; } } private void CreateEntryUi(HealthBarEntry entry) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_canvas == (Object)null)) { GameObject val = new GameObject("ECT Health Bar", new Type[2] { typeof(RectTransform), typeof(CanvasGroup) }); ((Object)val).hideFlags = (HideFlags)61; val.transform.SetParent(((Component)_canvas).transform, false); entry.Root = val; entry.RootRect = val.GetComponent<RectTransform>(); entry.RootRect.anchorMin = Vector2.zero; entry.RootRect.anchorMax = Vector2.zero; entry.RootRect.pivot = new Vector2(0.5f, 0.5f); entry.CanvasGroup = val.GetComponent<CanvasGroup>(); entry.CanvasGroup.alpha = 1f; entry.CanvasGroup.blocksRaycasts = false; entry.CanvasGroup.interactable = false; entry.Outer = CreateImage(val.transform, "Outer Border", OuterBorder); entry.Inner = CreateImage(val.transform, "Inner Border", InnerBorder); entry.Background = CreateImage(val.transform, "Missing Health", EmptyGrey); entry.DamageBar = CreateImage(val.transform, "Recent Damage", DamageRed); entry.CurrentBar = CreateImage(val.transform, "Current Health", NormalOrange); ConfigureFill(entry.DamageBar); ConfigureFill(entry.CurrentBar); entry.Label = CreateText(val.transform, "Health Text", (TextAlignmentOptions)513, Color.white); entry.DamageLabel = CreateText(val.transform, "Damage Text", (TextAlignmentOptions)513, DamageRed); entry.NameLabel = CreateText(val.transform, "Name Text", (TextAlignmentOptions)514, Color.white); } } private Image CreateImage(Transform parent, string name, Color color) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(Image) }) { hideFlags = (HideFlags)61 }; val.transform.SetParent(parent, false); Image component = val.GetComponent<Image>(); component.sprite = _whiteSprite; ((Graphic)component).color = color; ((Graphic)component).raycastTarget = false; return component; } private TextMeshProUGUI CreateText(Transform parent, string name, TextAlignmentOptions alignment, Color color) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }) { hideFlags = (HideFlags)61 }; val.transform.SetParent(parent, false); TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>(); ((Graphic)component).raycastTarget = false; ((TMP_Text)component).richText = true; ((TMP_Text)component).alignment = alignment; ((Graphic)component).color = color; ((TMP_Text)component).fontStyle = (FontStyles)1; if ((Object)(object)_font != (Object)null) { ((TMP_Text)component).font = _font; } ((TMP_Text)component).enableWordWrapping = false; return component; } private void ConfigureFill(Image image) { image.type = (Type)3; image.fillMethod = (FillMethod)0; image.fillOrigin = 0; image.fillAmount = 1f; } private void UpdateBars() { //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_031e: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_03f0: Unknown result type (might be due to invalid IL or missing references) //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_042f: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Unknown result type (might be due to invalid IL or missing references) //IL_045f: Unknown result type (might be due to invalid IL or missing references) //IL_0474: Unknown result type (might be due to invalid IL or missing references) //IL_048f: Unknown result type (might be due to invalid IL or missing references) //IL_04a4: Unknown result type (might be due to invalid IL or missing references) //IL_04ad: Unknown result type (might be due to invalid IL or missing references) //IL_04c2: Unknown result type (might be due to invalid IL or missing references) //IL_04cb: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Unknown result type (might be due to invalid IL or missing references) //IL_04e9: Unknown result type (might be due to invalid IL or missing references) //IL_0522: Unknown result type (might be due to invalid IL or missing references) //IL_0581: Unknown result type (might be due to invalid IL or missing references) //IL_0597: Unknown result type (might be due to invalid IL or missing references) //IL_059c: Unknown result type (might be due to invalid IL or missing references) //IL_059d: Unknown result type (might be due to invalid IL or missing references) //IL_05a4: Unknown result type (might be due to invalid IL or missing references) //IL_05c6: Unknown result type (might be due to invalid IL or missing references) //IL_05e0: Unknown result type (might be due to invalid IL or missing references) //IL_067c: Unknown result type (might be due to invalid IL or missing references) //IL_0692: Unknown result type (might be due to invalid IL or missing references) //IL_0697: Unknown result type (might be due to invalid IL or missing references) //IL_0698: Unknown result type (might be due to invalid IL or missing references) //IL_069f: Unknown result type (might be due to invalid IL or missing references) //IL_06bd: Unknown result type (might be due to invalid IL or missing references) //IL_06d7: Unknown result type (might be due to invalid IL or missing references) //IL_0757: Unknown result type (might be due to invalid IL or missing references) //IL_076d: Unknown result type (might be due to invalid IL or missing references) //IL_0772: Unknown result type (might be due to invalid IL or missing references) //IL_0773: Unknown result type (might be due to invalid IL or missing references) //IL_077a: Unknown result type (might be due to invalid IL or missing references) //IL_0798: Unknown result type (might be due to invalid IL or missing references) //IL_07b2: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return; } _removeBuffer.Clear(); foreach (KeyValuePair<int, HealthBarEntry> bar in _bars) { HealthBarEntry value = bar.Value; if (value == null || (Object)(object)value.Source == (Object)null || (Object)(object)value.EnemyIdentifier == (Object)null || (Object)(object)value.Root == (Object)null) { _removeBuffer.Add(bar.Key); continue; } Type type = ((object)value.EnemyIdentifier).GetType(); bool num = _owner.ReadBoolMember(type, value.EnemyIdentifier, "dead", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); EnemyProfile profile = (string.IsNullOrEmpty(value.ProfileKey) ? _owner.GetHealthBarProfile(value.EnemyIdentifier, value.Source) : EnemyCatalog.GetProfileByKey(value.ProfileKey)); float num2 = ReadDisplayHealth(type, value.EnemyIdentifier, value.Source, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, profile); if (num || num2 <= 0f || !value.Source.activeInHierarchy) { _removeBuffer.Add(bar.Key); continue; } float num3 = (value.HealthModifier = Mathf.Max(0.01f, _owner.ReadFloatMember(type, value.EnemyIdentifier, "totalHealthModifier", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, value.HealthModifier))); if (!IsLikelySentinelHealth(num2)) { value.MaxHealth = Mathf.Max(value.MaxHealth, num2); } else { value.MaxHealth = Mathf.Max(value.MaxHealth, ResolveDisplayMaxHealth(type, value.EnemyIdentifier, value.Source, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, profile, num2)); } float num4 = Mathf.Max(0f, Mathf.Min(num2, value.MaxHealth) * num3); float num5 = Mathf.Max(0.01f, value.MaxHealth * num3); float num6 = Mathf.Clamp01(num4 / num5); if (num4 < value.LastShownHealth - 0.01f) { float num7 = Mathf.Min(value.LastShownHealth - num4, Mathf.Max(value.LastShownHealth, 0f)); if (value.TimeSinceDamaged <= 4f) { value.RecentDamage += num7; } else { value.RecentDamage = num7; } value.TimeSinceDamaged = 0f; value.DamageFill = Mathf.Max(value.DamageFill, Mathf.Clamp01(value.LastShownHealth / num5)); } else if (num4 > value.LastShownHealth + 0.01f) { value.RecentDamage = 0f; value.TimeSinceDamaged = 0f; value.DamageFill = num6; } value.LastShownHealth = num4; value.TimeSinceDamaged += Time.deltaTime; value.DamageFill = Mathf.Max(num6, Mathf.MoveTowards(value.DamageFill, num6, Time.deltaTime * Mathf.Lerp(1f, 10f, Mathf.Max(0f, value.DamageFill - num6)))); Vector3 barWorldPosition = GetBarWorldPosition(value.Source); float num8 = Mathf.Max(1f, _owner._healthBarsMaxDrawDistance.Value); if (Vector3.Distance(((Component)main).transform.position, barWorldPosition) > num8) { value.CanvasGroup.alpha = 0f; continue; } Vector3 val = main.WorldToScreenPoint(barWorldPosition); if (val.z <= 0f) { value.CanvasGroup.alpha = 0f; continue; } value.CanvasGroup.alpha = 1f; float num9 = Mathf.Clamp(_owner._healthBarsScale.Value, 0.25f, 4f); float num10 = Mathf.Ceil(Mathf.Pow(num5, 0.55f) * 12.5f * num9); num10 = Mathf.Clamp(num10, 42f * num9, 620f * num9); float num11 = Mathf.Clamp(7f * num9, 4f, 22f); ((Transform)value.RootRect).position = val + new Vector3(0f, 12f * num9, 0f); value.RootRect.sizeDelta = new Vector2(num10 + 4f * num9, num11 + 28f * num9); SetRect(((Graphic)value.Outer).rectTransform, Vector2.zero, new Vector2(num10 + 4f * num9, num11 + 4f * num9)); SetRect(((Graphic)value.Inner).rectTransform, Vector2.zero, new Vector2(num10 + 2f * num9, num11 + 2f * num9)); SetRect(((Graphic)value.Background).rectTransform, Vector2.zero, new Vector2(num10, num11)); SetRect(((Graphic)value.DamageBar).rectTransform, Vector2.zero, new Vector2(num10, num11)); SetRect(((Graphic)value.CurrentBar).rectTransform, Vector2.zero, new Vector2(num10, num11)); value.DamageBar.fillAmount = Mathf.Clamp01(value.DamageFill); value.CurrentBar.fillAmount = num6; ((Graphic)value.CurrentBar).color = GetBarColor(value, type, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_owner._healthBarsShowNumbers.Value) { ((Component)value.Label).gameObject.SetActive(true); ((TMP_Text)value.Label).fontSize = Mathf.RoundToInt(12f * num9); RectTransform rectTransform = ((TMP_Text)value.Label).rectTransform; rectTransform.pivot = new Vector2(0f, 0f); Vector2 anchorMin = (rectTransform.anchorMax = new Vector2(0.5f, 0.5f)); rectTransform.anchorMin = anchorMin; rectTransform.anchoredPosition = new Vector2((0f - num10) * 0.5f, num11 * 0.5f + 2f * num9); rectTransform.sizeDelta = new Vector2(num10 * 1.4f, 18f * num9); ((TMP_Text)value.Label).text = FormatHealthRich(num4, num5); } else { ((Component)value.Label).gameObject.SetActive(false); } bool flag = value.RecentDamage > 0.01f && value.TimeSinceDamaged <= 4f; ((Component)value.DamageLabel).gameObject.SetActive(flag); if (flag) { ((TMP_Text)value.DamageLabel).fontSize = Mathf.RoundToInt(12f * num9); RectTransform rectTransform2 = ((TMP_Text)value.DamageLabel).rectTransform; rectTransform2.pivot = new Vector2(0f, 0.5f); Vector2 anchorMin = (rectTransform2.anchorMax = new Vector2(0.5f, 0.5f)); rectTransform2.anchorMin = anchorMin; rectTransform2.anchoredPosition = new Vector2(num10 * 0.5f + 6f * num9, 0f); rectTransform2.sizeDelta = new Vector2(110f * num9, 22f * num9); ((TMP_Text)value.DamageLabel).text = "-" + FormatDamage(value.RecentDamage); } if (_owner._healthBarsShowNames.Value) { ((Component)value.NameLabel).gameObject.SetActive(true); ((TMP_Text)value.NameLabel).fontSize = Mathf.RoundToInt(11f * num9); RectTransform rectTransform3 = ((TMP_Text)value.NameLabel).rectTransform; rectTransform3.pivot = new Vector2(0.5f, 0f); Vector2 anchorMin = (rectTransform3.anchorMax = new Vector2(0.5f, 0.5f)); rectTransform3.anchorMin = anchorMin; rectTransform3.anchoredPosition = new Vector2(0f, num11 * 0.5f + 18f * num9); rectTransform3.sizeDelta = new Vector2(num10 * 1.5f, 18f * num9); ((TMP_Text)value.NameLabel).text = value.DisplayName; } else { ((Component)value.NameLabel).gameObject.SetActive(false); } } for (int i = 0; i < _removeBuffer.Count; i++) { if (_bars.TryGetValue(_removeBuffer[i], out var value2) && value2 != null && (Object)(object)value2.Root != (Object)null) { Object.Destroy((Object)(object)value2.Root); } _bars.Remove(_removeBuffer[i]); } } private static void SetRect(RectTransform rect, Vector2 anchoredPosition, Vector2 size) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) rect.pivot = new Vector2(0.5f, 0.5f); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.5f); rect.anchorMax = val; rect.anchorMin = val; rect.anchoredPosition = anchoredPosition; rect.sizeDelta = size; } private Vector3 GetBarWorldPosition(GameObject source) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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) //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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) Vector3 position = source.transform.position; Collider componentInChildren = source.GetComponentInChildren<Collider>(); if ((Object)(object)componentInChildren != (Object)null) { Bounds bounds = componentInChildren.bounds; Vector3 center = ((Bounds)(ref bounds)).center; Vector3 up = Vector3.up; bounds = componentInChildren.bounds; return center + up * (((Bounds)(ref bounds)).extents.y + _owner._healthBarsHeightOffset.Value); } return position + Vector3.up * (2f + _owner._healthBarsHeightOffset.Value); } private Color GetBarColor(HealthBarEntry bar, Type type, BindingFlags flags) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (_owner.ReadBoolMember(type, bar.EnemyIdentifier, "blessed", flags)) { return BlessedBlue; } if (bar.IsBoss) { return BossRed; } return NormalOrange; } private string FormatHealthRich(float health, float max) { int num = Mathf.CeilToInt(Mathf.Max(health * 100f, 0f)); int num2 = Mathf.CeilToInt(Mathf.Max(max * 100f, 0f)); return num + "<space=0.25em><size=75%><color=#FFFA>" + num2 + "</color></size>"; } private string FormatDamage(float damage) { return Mathf.FloorToInt(Mathf.Max(damage * 100f, 0f)).ToString(); } private static bool IsLikelySentinelHealth(float health) { return health >= 900f; } private float ReadDisplayHealth(Type type, Component enemyIdentifier, GameObject source, BindingFlags flags, EnemyProfile profile) { float num = Mathf.Max(0f, _owner.ReadFloatMember(type, enemyIdentifier, "health", flags, 0f)); if (!IsLikelySentinelHealth(num)) { return num; } if (TryReadPlausibleHealthFromComponents(source, type, out var value)) { return value; } return ResolveDisplayMaxHealth(type, enemyIdentifier, source, flags, profile, num); } private float ResolveDisplayMaxHealth(Type type, Component enemyIdentifier, GameObject source, BindingFlags flags, EnemyProfile profile, float currentHealth) { float num = Mathf.Max(0f, _owner.ReadFloatMember(type, enemyIdentifier, "maxHealth", flags, 0f)); num = Mathf.Max(num, _owner.ReadFloatMember(type, enemyIdentifier, "totalHealth", flags, 0f)); num = Mathf.Max(num, _owner.ReadFloatMember(type, enemyIdentifier, "fullHealth", flags, 0f)); if (num > 0.01f && !IsLikelySentinelHealth(num)) { return num; } if (!IsLikelySentinelHealth(currentHealth)) { return Mathf.Max(0.01f, currentHealth); } float knownBaseHealth = GetKnownBaseHealth(profile.Key); if (knownBaseHealth > 0f) { return knownBaseHealth; } if (TryReadPlausibleHealthFromComponents(source, type, out var value)) { return value; } return 1f; } private bool TryReadPlausibleHealthFromComponents(GameObject source, Type enemyIdentifierType, out float value) { value = 0f; if ((Object)(object)source == (Object)null) { return false; } Component[] componentsInChildren = source.GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } Type type = ((object)val).GetType(); if (enemyIdentifierType != null && type == enemyIdentifierType) { continue; } FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (LooksLikeHealthMember(fieldInfo.Name)) { object value2; try { value2 = fieldInfo.GetValue(val); } catch { continue; } if (TryCoercePlausibleHealth(value2, out value)) { return true; } } } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.CanRead && LooksLikeHealthMember(propertyInfo.Name)) { object value3; try { value3 = propertyInfo.GetValue(val, null); } catch { continue; } if (TryCoercePlausibleHealth(value3, out value)) { return true; } } } } return false; } private static bool LooksLikeHealthMember(string name) { string text = Normalize(name); if (!(text == "health") && !(text == "hp") && !text.Contains("currenthealth")) { return text.Contains("healthpoints"); } return true; } private static bool TryCoercePlausibleHealth(object raw, out float value) { value = 0f; if (raw == null) { return false; } if (raw is float) { value = (float)raw; } else if (raw is int) { value = (int)raw; } else { if (!(raw is double)) { return false; } value = (float)(double)raw; } if (value > 0.01f) { return value < 900f; } return false; } private static float GetKnownBaseHealth(string profileKey) { return Normalize(profileKey) switch { "filth" => 0.5f, "stray" => 1.5f, "drone" => 2f, "schism" => 5f, "soldier" => 2f, "streetcleaner" => 4.5f, "stalker" => 3f, "mannequin" => 3f, "virtue" => 10f, "sentry" => 8f, "maurice" => 10f, "swordsmachine" => 15f, "cerberus" => 15f, "gutterman" => 18f, "providence" => 8f, "guttertank" => 18f, "mindflayer" => 20f, "power" => 10f, "ferryman" => 30f, "hideousmass" => 30f, "insurrectionist" => 50f, "minotaur" => 50f, "mirrorreaper" => 30f, _ => 0f, }; } private void ClearBars() { foreach (KeyValuePair<int, HealthBarEntry> bar in _bars) { if (bar.Value != null && (Object)(object)bar.Value.Root != (Object)null) { Object.Destroy((Object)(object)bar.Value.Root); } } _bars.Clear(); _removeBuffer.Clear(); } } private sealed class HealthBarEntry { public readonly GameObject Source; public readonly Component EnemyIdentifier; public readonly string DisplayName; public readonly bool IsBoss; public readonly string ProfileKey; public GameObject Root; public RectTransform RootRect; public CanvasGroup CanvasGroup; public Image Outer; public Image Inner; public Image Background; public Image DamageBar; public Image CurrentBar; public TextMeshProUGUI Label; public TextMeshProUGUI DamageLabel; public TextMeshProUGUI NameLabel; public float MaxHealth; public float HealthModifier; public float DamageFill = 1f; public float LastShownHealth; public float RecentDamage; public float TimeSinceDamaged; public HealthBarEntry(GameObject source, Component enemyIdentifier, float maxHealth, float healthModifier, string displayName, bool isBoss, string profileKey) { Source = source; EnemyIdentifier = enemyIdentifier; MaxHealth = Mathf.Max(0.01f, maxHealth); HealthModifier = Mathf.Max(0.01f, healthModifier); DisplayName = displayName; IsBoss = isBoss; ProfileKey = profileKey ?? string.Empty; LastShownHealth = MaxHealth * HealthModifier; TimeSinceDamaged = 0f; } } [CompilerGenerated] private sealed class <DestroyOriginalAfterReplacementValid>d__120 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public GameObject source; public EasyCampaignTweaksPlugin <>4__this; public GameObject clone; public string key; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DestroyOriginalAfterReplacementValid>d__120(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown int num = <>1__state; EasyCampaignTweaksPlugin easyCampaignTweaksPlugin = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(0.2f); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)source == (Object)null) { return false; } if (easyCampaignTweaksPlugin.IsSpawnedCloneValid(clone, key)) { Object.Destroy((Object)(object)source); } else if ((Object)(object)clone != (Object)null) { Object.Destroy((Object)(object)clone); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const string PluginGuid = "twistedscarlett.ultrakill.easycampaigntweaks"; public const string PluginName = "Easy Campaign Tweaks"; public const string PluginVersion = "1.1.3"; internal static EasyCampaignTweaksPlugin Instance; internal static ManualLogSource Log; private Harmony _harmony; private Type _enemyIdentifierType; private Type _gunControlType; private Type _dualWieldType; private Type _newMovementType; private Component _cachedMovement; private Rigidbody _cachedMovementRigidbody; private HealthBarManager _healthBarManager; private readonly Dictionary<string, FieldInfo> _fieldCache = new Dictionary<string, FieldInfo>(); private readonly Dictionary<string, PropertyInfo> _propertyCache = new Dictionary<string, PropertyInfo>(); private readonly Dictionary<string, MethodInfo> _methodCache = new Dictionary<string, MethodInfo>(); private ConfigEntry<bool> _debugLogging; private ConfigEntry<float> _spawnDistanceMin; private ConfigEntry<float> _spawnDistanceMax; private ConfigEntry<bool> _mitosisEnabled; private ConfigEntry<float> _mitosisMultiplier; private ConfigEntry<bool> _enrageEnabled; private ConfigEntry<float> _enrageChancePercent; private ConfigEntry<bool> _radianceEnabled; private ConfigEntry<float> _radianceChancePercent; private ConfigEntry<float> _radianceTier; private ConfigEntry<bool> _radianceUseCustomMultipliers; private ConfigEntry<float> _radianceSpeedMultiplier; private ConfigEntry<float> _radianceHealthMultiplier; private ConfigEntry<float> _radianceDamageMultiplier; private ConfigEntry<bool> _radianceAffectsDamage; private ConfigEntry<bool> _sandifyEnabled; private ConfigEntry<float> _sandifyChancePercent; private ConfigEntry<bool> _tankifyEnabled; private ConfigEntry<float> _tankifyChancePercent; private ConfigEntry<float> _tankifyHealthMultiplier; private ConfigEntry<bool> _strongerEnabled; private ConfigEntry<float> _strongerChancePercent; private ConfigEntry<int> _strongerMinimumTierIncrease; private ConfigEntry<int> _strongerMaximumTierIncrease; private ConfigEntry<int> _strongerMaxReplacementsPerWave; private ConfigEntry<int> _strongerAntiSoftlockFilthCount; private ConfigEntry<bool> _randomizerEnabled; private ConfigEntry<float> _randomizerChancePercent; private ConfigEntry<int> _maxRandomizerReplacementsPerWave; private ConfigEntry<int> _randomizerAntiSoftlockFilthCount; private ConfigEntry<bool> _healthBarsEnabled; private ConfigEntry<float> _healthBarsMinHealth; private ConfigEntry<bool> _healthBarsShowBosses; private ConfigEntry<bool> _healthBarsShowNames; private ConfigEntry<bool> _healthBarsShowNumbers; private ConfigEntry<float> _healthBarsScale; private ConfigEntry<float> _healthBarsHeightOffset; private ConfigEntry<float> _healthBarsMaxDrawDistance; private ConfigEntry<bool> _ultrahotEnabled; private ConfigEntry<float> _ultrahotVelocityForNormalTime; private ConfigEntry<float> _ultrahotMinimumTimeScale; private ConfigEntry<float> _ultrahotMaximumTimeScale; private ConfigEntry<float> _ultrahotSmoothing; private ConfigEntry<bool> _dualWieldEnabled; private ConfigEntry<int> _dualWieldStacks; private ConfigEntry<float> _dualWieldDuration; private ConfigEntry<float> _dualWieldRefreshSeconds; private readonly HashSet<int> _processedEnemies = new HashSet<int>(); private readonly List<PendingWaveEnemy> _pendingWave = new List<PendingWaveEnemy>(); private readonly Dictionary<string, EnemyTemplatePool> _scenePools = new Dictionary<string, EnemyTemplatePool>(); private readonly Dictionary<string, EnemyTemplatePool> _globalPools = new Dictionary<string, EnemyTemplatePool>(); private readonly HashSet<string> _loggedBlocked = new HashSet<string>(); private float _pendingWaveDeadline = -1f; private float _sceneLoadedTime; private string _currentSceneName = string.Empty; private bool _ultrahotWasActive; private bool _globalPoolScanned; private float _nextDualWieldRefresh; private const int DefaultMaxLivingModdedEnemies = 96; private const bool AffectCyberGrind = false; private const float SceneStartGraceSeconds = 1f; private const float SpawnWaveReadSeconds = 1.25f; private const float LowTierSpawnWaveReadSeconds = 0.45f; private const int ProcessWaveImmediatelyAtSize = 5; private const int LargeEnemyMinimumNonFilthWaveSize = 5; private const float LargeEnemyRarityMultiplier = 0.5f; private const float LargeEnemyGroundCheckDistance = 10f; private void Awake() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; CacheCommonTypes(); BindConfig(); _healthBarManager = ((Component)this).gameObject.AddComponent<HealthBarManager>(); _healthBarManager.Initialize(this); _sceneLoadedTime = Time.time; Scene activeScene = SceneManager.GetActiveScene(); _currentSceneName = ((Scene)(ref activeScene)).name ?? string.Empty; SceneManager.sceneLoaded += OnSceneLoaded; _harmony = new Harmony("twistedscarlett.ultrakill.easycampaigntweaks"); PatchSafely(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Easy Campaign Tweaks loaded. Gameplay modifiers are disabled by default."); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; if (_ultrahotWasActive) { Time.timeScale = 1f; } if ((Object)(object)_healthBarManager != (Object)null) { Object.Destroy((Object)(object)_healthBarManager); } try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to unpatch cleanly: " + ex.Message)); } } private void CacheCommonTypes() { _enemyIdentifierType = AccessTools.TypeByName("EnemyIdentifier"); _gunControlType = AccessTools.TypeByName("GunControl"); _dualWieldType = AccessTools.TypeByName("DualWield"); _newMovementType = AccessTools.TypeByName("NewMovement"); } private ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description) { return ((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, description); } private void BindConfig() { _spawnDistanceMin = Bind("Mitosis", "SpawnDistanceMin", 2f, "Minimum offset used for Mitosis duplicates."); _spawnDistanceMax = Bind("Mitosis", "SpawnDistanceMax", 5.5f, "Maximum offset used for Mitosis duplicates."); _mitosisEnabled = Bind("Mitosis", "Enabled", defaultValue: false, "Duplicate eligible enemy spawns."); _mitosisMultiplier = Bind("Mitosis", "EnemySpawnMultiplier", 2f, "Total enemy spawn multiplier. 2.0 means one extra copy per natural enemy on average."); _enrageEnabled = Bind("Enraged", "Enabled", defaultValue: false, "Enable Enraged module."); _enrageChancePercent = Bind("Enraged", "EnrageChancePercent", 100f, "Chance from 0 to 100 for eligible enemies to become enraged."); _radianceEnabled = Bind("Radiant", "Enabled", defaultValue: false, "Enable Radiant module."); _radianceChancePercent = Bind("Radiant", "RadiantChancePercent", 100f, "Chance from 0 to 100 for eligible enemies to receive radiance."); _radianceTier = Bind("Radiant", "RadianceTier", 1f, "Radiance tier. Default 1 is tier-1 radiance. Values above 1 increase the game's radiance tier scaling."); _radianceUseCustomMultipliers = Bind("Radiant", "UseCustomMultipliers", defaultValue: false, "If false, use vanilla-style tier-1 per-enemy radiance presets. If true, use the custom Speed/Health/Damage multipliers below."); _radianceSpeedMultiplier = Bind("Radiant", "CustomSpeedMultiplier", 1.5f, "Only used when UseCustomMultipliers is true."); _radianceHealthMultiplier = Bind("Radiant", "CustomHealthMultiplier", 1.5f, "Only used when UseCustomMultipliers is true."); _radianceDamageMultiplier = Bind("Radiant", "CustomDamageMultiplier", 1.5f, "Only used when UseCustomMultipliers is true and RadianceAffectsDamage is enabled."); _radianceAffectsDamage = Bind("Radiant", "RadianceAffectsDamage", defaultValue: true, "If true, radiant enemies receive the damage buff. Disable for Cyber Grind-style no-damage radiance."); _sandifyEnabled = Bind("Sandify", "Enabled", defaultValue: false, "Enable Sandify module."); _sandifyChancePercent = Bind("Sandify", "SandifyChancePercent", 100f, "Chance from 0 to 100 for eligible enemies to spawn sanded."); _tankifyEnabled = Bind("Tankify", "Enabled", defaultValue: false, "Enable Tankify module."); _tankifyChancePercent = Bind("Tankify", "TankifyChancePercent", 100f, "Chance from 0 to 100 for eligible enemies to have altered health."); _tankifyHealthMultiplier = Bind("Tankify", "HealthMultiplier", 2f, "Enemy health multiplier. Values below 1 make enemies frailer; values above 1 make them tankier."); _strongerEnabled = Bind("Stronger Enemies", "Enabled", defaultValue: false, "Replace eligible enemies with stronger enemies from the protected global pool."); _strongerChancePercent = Bind("Stronger Enemies", "ReplacementChancePercent", 100f, "Chance from 0 to 100 for each eligible enemy in a wave to be considered for a stronger replacement."); _strongerMinimumTierIncrease = Bind("Stronger Enemies", "MinimumTierIncrease", 1, "Minimum tier increase. Default 1 means at least the next stronger tier."); _strongerMaximumTierIncrease = Bind("Stronger Enemies", "MaximumTierIncrease", 1, "Preferred maximum tier increase. If this is higher than the minimum, the target tier jump is randomized."); _strongerMaxReplacementsPerWave = Bind("Stronger Enemies", "MaxReplacementsPerWave", 25, "Hard cap for Stronger Enemies replacements in a single spawn wave."); _strongerAntiSoftlockFilthCount = Bind("Stronger Enemies", "AntiSoftlockFilth", 2, "After this module replaces enemies in a wave of 3+ natural enemies, spawn this many extra Filth as softlock insurance. Set to 0 to disable."); _randomizerEnabled = Bind("Safe Randomizer", "Enabled", defaultValue: false, "Randomize natural enemy spawns using protected templates. Malicious Faces are always protected."); _randomizerChancePercent = Bind("Safe Randomizer", "ReplacementChancePercent", 100f, "Chance from 0 to 100 for each eligible natural enemy in a wave to be replaced."); _maxRandomizerReplacementsPerWave = Bind("Safe Randomizer", "MaxReplacementsPerWave", 25, "Hard cap for Safe Randomizer replacements in a single wave."); _randomizerAntiSoftlockFilthCount = Bind("Safe Randomizer", "AntiSoftlockFilth", 2, "After this module replaces enemies in a wave of 3+ natural enemies, spawn this many extra Filth as softlock insurance. Set to 0 to disable."); _healthBarsEnabled = Bind("Health Bars", "Enabled", defaultValue: false, "Show compact health bars above enemies."); _healthBarsMinHealth = Bind("Health Bars", "MinimumBaseHealth", 0f, "Only show bars for enemies with at least this much base health. 0 shows all eligible enemies."); _healthBarsShowBosses = Bind("Health Bars", "ShowBosses", defaultValue: false, "Show Easy Campaign Tweaks health bars for boss enemies. Bosses already have vanilla bars, so this is disabled by default."); _healthBarsShowNames = Bind("Health Bars", "ShowEnemyNames", defaultValue: false, "Show the enemy name above each health bar."); _healthBarsShowNumbers = Bind("Health Bars", "ShowHealthNumbers", defaultValue: true, "Show current and maximum health numbers on the bar."); _healthBarsScale = Bind("Health Bars", "Scale", 1.6f, "Global health bar size multiplier."); _healthBarsHeightOffset = Bind("Health Bars", "HeightOffset", 0.1f, "Extra vertical offset above the enemy."); _healthBarsMaxDrawDistance = Bind("Health Bars", "MaxDrawDistance", 160f, "Hide health bars farther than this distance from the camera. Set very high to effectively disable distance hiding."); _ultrahotEnabled = Bind("ULTRAHOT", "Enabled", defaultValue: false, "Enable ULTRAHOT: time moves based on player movement speed."); _ultrahotVelocityForNormalTime = Bind("ULTRAHOT", "VelocityForNormalTime", 20f, "Player velocity magnitude that maps to normal time scale."); _ultrahotMinimumTimeScale = Bind("ULTRAHOT", "MinimumTimeScale", 0.01f, "Slowest time scale while nearly still."); _ultrahotMaximumTimeScale = Bind("ULTRAHOT", "MaximumTimeScale", 1.25f, "Fastest time scale while moving quickly."); _ultrahotSmoothing = Bind("ULTRAHOT", "Smoothing", 15f, "How quickly time scale follows player movement."); _dualWieldEnabled = Bind("Dual Wield", "Enabled", defaultValue: false, "Always keep at least the configured number of Dual Wield powerups active."); _dualWieldStacks = Bind("Dual Wield", "Stacks", 1, "Minimum number of Dual Wield stacks to keep active. Stacks can be very strong."); _dualWieldDuration = Bind("Dual Wield", "PowerupDurationSeconds", 999999f, "Duration used when creating a maintained Dual Wield stack."); _dualWieldRefreshSeconds = Bind("Dual Wield", "RefreshCheckSeconds", 0.75f, "How often the mod checks whether it needs to add missing Dual Wield stacks."); _debugLogging = Bind("Debug", "DebugLogging", defaultValue: false, "Verbose logs for spawn-wave decisions and skipped enemies."); } private void PatchSafely() { PatchFirstMethod(GetEnemyIdentifierType(), "Start", "EnemyStartPostfix", required: true); } private void PatchFirstMethod(Type type, string methodName, string patchMethodName, bool required) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown if (type == null) { if (required) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not find type for required patch method " + methodName + ".")); } return; } MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { if (required) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not find required method " + type.Name + "." + methodName + ".")); } } else { HarmonyMethod val = new HarmonyMethod(typeof(EasyCampaignTweaksPlugin).GetMethod(patchMethodName, BindingFlags.Static | BindingFlags.NonPublic)); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Patched " + type.Name + "." + methodName + ".")); } } private static void EnemyStartPostfix(object __instance) { try { Instance?.OnEnemyStarted(__instance); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Enemy Start patch failed safely: " + ex.GetType().Name + ": " + ex.Message)); } } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { _sceneLoadedTime = Time.time; _currentSceneName = ((Scene)(ref scene)).name ?? string.Empty; _processedEnemies.Clear(); _pendingWave.Clear(); _pendingWaveDeadline = -1f; _scenePools.Clear(); _globalPools.Clear(); _globalPoolScanned = false; _cachedMovement = null; _cachedMovementRigidbody = null; _nextDualWieldRefresh = Time.time + 1f; if (_ultrahotWasActive) { Time.timeScale = 1f; } if (_debugLogging != null && _debugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Scene loaded: " + _currentSceneName)); } } private void Update() { if (NeedsWaveDirector()) { ProcessPendingWaveIfReady(); } else if (_pendingWave.Count > 0) { _pendingWave.Clear(); _pendingWaveDeadline = -1f; } UpdateUltraHot(); UpdateDualWieldMaintenance(); } private bool CanModifyCurrentScene() { string text = (_currentSceneName ?? string.Empty).ToLowerInvariant(); if (text.Contains("endless") || text.Contains("cyber") || text.Contains("grid")) { return false; } return true; } private bool NeedsWaveDirector() { if (_strongerEnabled == null || !_strongerEnabled.Value) { if (_randomizerEnabled != null) { return _randomizerEnabled.Value; } return false; } return true; } private bool NeedsEnemyEffectModifiers() { if ((_sandifyEnabled == null || !_sandifyEnabled.Value) && (_tankifyEnabled == null || !_tankifyEnabled.Value) && (_radianceEnabled == null || !_radianceEnabled.Value)) { if (_enrageEnabled != null) { return _enrageEnabled.Value; } return false; } return true; } private void OnEnemyStarted(object enemyObject) { if (!CanModifyCurrentScene() || enemyObject == null) { return; } Component val = (Component)((enemyObject is Component) ? enemyObject : null); if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null) { return; } GameObject gameObject = val.gameObject; if (!gameObject.activeInHierarchy) { return; } int instanceID = ((Object)gameObject).GetInstanceID(); if (_processedEnemies.Contains(instanceID)) { return; } _processedEnemies.Add(instanceID); if ((Object)(object)gameObject.GetComponent<EcmSpawnedMarker>() != (Object)null || ((Object)gameObject).name.IndexOf("[ECM", StringComparison.OrdinalIgnoreCase) >= 0) { ApplyConfiguredEnemyEffects(gameObject, isModdedEnemy: true); TryTrackHealthBar(val, gameObject); return; } TryTrackHealthBar(val, gameObject); if (IsHardBlockedTemplateName(((Object)gameObject).name)) { LogBlocked(string.Empty, ((Object)gameObject).name, "hard-blocked name"); return; } string text = ReadEnemyTypeName(enemyObject, gameObject); EnemyProfile profile = EnemyCatalog.GetProfile(text, ((Object)gameObject).name, allowUnknown: false, allowExperimentalLarge: true); if (!profile.Allowed) { LogBlocked(text, ((Object)gameObject).name, profile.BlockReason); return; } if (NeedsWaveDirector()) { RegisterTemplate(_scenePools, profile, gameObject, allowInactiveTemplates: false); RegisterTemplate(_globalPools, profile, gameObject, allowInactiveTemplates: false); } ApplyConfiguredEnemyEffects(gameObject, isModdedEnemy: false); TryApplyImmediateMitosis(gameObject, profile); if (NeedsWaveDirector()) { QueueNaturalEnemyForWave(gameObject, profile); } } private void TryTrackHealthBar(Component enemyIdentifier, GameObject source) { if (!((Object)(object)_healthBarManager == (Object)null) && _healthBarsEnabled != null && _healthBarsEnabled.Value) { _healthBarManager.Track(enemyIdentifier, source); } } private void QueueNaturalEnemyForWave(GameObject source, EnemyProfile profile) { if (!((Object)(object)source == (Object)null) && profile.Allowed) { _pendingWave.Add(new PendingWaveEnemy(source, profile, Time.time)); RefreshPendingWaveDeadline(); } } private void RefreshPendingWaveDeadline() { if (_pendingWave.Count == 0) { _pendingWaveDeadline = -1f; return; } bool flag = true; for (int i = 0; i < _pendingWave.Count; i++) { PendingWaveEnemy pendingWaveEnemy = _pendingWave[i]; if (pendingWaveEnemy == null || !IsFastFodder(pendingWaveEnemy.Profile.Key)) { flag = false; break; } } float num = Mathf.Clamp(1.25f, 0.05f, 6f); float num2 = Mathf.Clamp(0.45f, 0.05f, num); float num3 = (flag ? num2 : num); int num4 = Mathf.Max(2, 5); if (_pendingWave.Count >= num4) { num3 = Mathf.Min(num3, 0.12f); } _pendingWaveDeadline = Time.time + num3; } private bool IsFastFodder(string key) { key = Normalize(key); if (!(key == "filth") && !(key == "stray")) { return key == "drone"; } return true; } private void ProcessPendingWaveIfReady() { if (!NeedsWaveDirector()) { if (_pendingWave.Count > 0) { _pendingWave.Clear(); _pendingWaveDeadline = -1f; } } else { if (_pendingWave.Count == 0 || Time.time < _pendingWaveDeadline) { return; } float num = Mathf.Max(0f, 1f); if (Time.time - _sceneLoadedTime < num) { _pendingWaveDeadline = Time.time + 0.25f; return; } List<PendingWaveEnemy> list = new List<PendingWaveEnemy>(); for (int i = 0; i < _pendingWave.Count; i++) { PendingWaveEnemy pendingWaveEnemy = _pendingWave[i]; if (pendingWaveEnemy != null && pendingWaveEnemy.Profile.Allowed && (!((Object)(object)pendingWaveEnemy.Source != (Object)null) || !((Object)(object)pendingWaveEnemy.Source.GetComponent<EcmSpawnedMarker>() != (Object)null))) { list.Add(pendingWaveEnemy); } } _pendingWave.Clear(); _pendingWaveDeadline = -1f; if (list.Count == 0) { return; } if (list.Count < 2) { if (_debugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"ECM wave ignored by replacement modules because it only contained one enemy."); } return; } EnsureGlobalPoolScanned(); HashSet<int> replacedIds = new HashSet<int>(); int nonFilthCount = CountNonFilthEnemies(list); int num2 = TryApplyStrongerEnemies(list, replacedIds, nonFilthCount); int num3 = TryApplySafeRandomizer(list, replacedIds, nonFilthCount); if (_debugLogging.Value && (num2 > 0 || num3 > 0)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ECM wave: natural=" + list.Count + ", stronger=" + num2 + ", random=" + num3 + ".")); } } } private Dictionary<string, EnemyTemplatePool> GetPreferredReplacementPools() { EnsureGlobalPoolScanned(); if (_globalPools.Count > 1) { return _globalPools; } return _scenePools; } private void EnsureGlobalPoolScanned() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (_globalPoolScanned) { return; } _globalPoolScanned = true; Type enemyIdentifierType = GetEnemyIdentifierType(); if (enemyIdentifierType == null) { return; } int num = 0; try { Object[] array = Resources.FindObjectsOfTypeAll(enemyIdentifierType); foreach (Object obj in array) { Component val = (Component)(object)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null) { continue; } GameObject gameObject = val.gameObject; Scene scene = gameObject.scene; if ((((Scene)(ref scene)).IsValid() && (Object)(object)gameObject.GetComponent<EcmSpawnedMarker>() != (Object)null) || IsHardBlockedTemplateName(((Object)gameObject).name)) { continue; } EnemyProfile profile = EnemyCatalog.GetProfile(ReadEnemyTypeName(val, gameObject), ((Object)gameObject).name, allowUnknown: false, allowExperimentalLarge: true); if (profile.Allowed && !(profile.Key == "maurice")) { int count = _globalPools.Count; RegisterTemplate(_globalPools, profile, gameObject, allowInactiveTemplates: true); if (_globalPools.Count > count) { num++; } } } } catch (Exception ex) { if (_debugLogging != null && _debugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ECM global enemy template scan skipped safely: " + ex.Message)); } } if (_debugLogging != null && _debugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ECM protected global pool contains " + _globalPools.Count + " enemy types after scan.")); } } private int TryApplyStrongerEnemies(List<PendingWaveEnemy> wave, HashSet<int> replacedIds, int nonFilthCount) { if (wave == null || wave.Count < 2) { return 0; } if (!_strongerEnabled.Value) { return 0; } if (EcmSpawnedMarker.ActiveCount >= 96) { return 0; } float num = Mathf.Clamp01(_strongerChancePercent.Value / 100f); int num2 = Mathf.Max(0, _strongerMaxReplacementsPerWave.Value); if (num <= 0f || num2 <= 0) { return 0; } int num3 = Mathf.Max(1, _strongerMinimumTierIncrease.Value); int maxIncrease = Mathf.Max(num3, _strongerMaximumTierIncrease.Value); Dictionary<string, EnemyTemplatePool> preferredReplacementPools = GetPreferredReplacementPools(); if (preferredReplacementPools.Count <= 1) { return 0; } List<PendingWaveEnemy> list = CopyWave(wave); Shuffle(list); int num4 = 0; for (int i = 0; i < list.Count; i++) { if (num4 >= num2) { break; } PendingWaveEnemy pendingWaveEnemy = list[i]; if (pendingWaveEnemy != null && !replacedIds.Contains(pendingWaveEnemy.SourceId) && !(Random.value > num) && IsEntrySourceAlive(pendingWaveEnemy) && !IsMaurice(pendingWaveEnemy.Profile) && !ShouldAvoidCloningOrReplacingSource(pendingWaveEnemy.Source)) { EnemySpawnCandidate enemySpawnCandidate = ChooseStrongerCandidate(pendingWaveEnemy.Profile, pendingWaveEnemy.Source, preferredReplacementPools, nonFilthCount, num3, maxIncrease); if (enemySpawnCandidate != null && SpawnReplacement(pendingWaveEnemy, enemySpawnCandidate, keepOriginalIfFails: true, "Stronger")) { replacedIds.Add(pendingWaveEnemy.SourceId); num4++; } } } if (num4 > 0) { SpawnAntiSoftlockFilth(wave, _strongerAntiSoftlockFilthCount.Value, "Stronger"); } return num4; } private EnemySpawnCandidate ChooseStrongerCandidate(EnemyProfile sourceProfile, GameObject source, Dictionary<string, EnemyTemplatePool> pools, int nonFilthCount, int minIncrease, int maxIncrease) { int tier = sourceProfile.Tier; if (tier <= 0 || pools == null || pools.Count == 0) { return null; } minIncrease = Mathf.Max(1, minIncrease); maxIncrease = Mathf.Max(minIncrease, maxIncrease); List<int> list = BuildTierIncreaseOrder(minIncrease, maxIncrease); for (int i = 0; i < list.Count; i++) { int targetTier = tier + list[i]; List<WeightedCandidate> weighted = new List<WeightedCandidate>(); BuildStrongerCandidateList(weighted, sourceProfile, source, pools, nonFilthCount, targetTier); EnemySpawnCandidate enemySpawnCandidate = PickWeightedCandidate(weighted); if (enemySpawnCandidate != null) { return enemySpawnCandidate; } } if (_debugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ECM Stronger found no usable tier-up candidate for " + sourceProfile.Key + " in the configured range +" + minIncrease + " to +" + maxIncrease + ".")); } return null; } private List<int> BuildTierIncreaseOrder(int minIncrease, int maxIncrease) { List<int> list = new List<int>(); for (int i = minIncrease; i <= maxIncrease; i++) { list.Add(i); } if (list.Count > 1) { ShuffleInts(list); } return list; } private void ShuffleInts(List<int> list) { if (list != null) { for (int num = list.Count - 1; num > 0; num--) { int index = Random.Range(0, num + 1); int value = list[num]; list[num] = list[index]; list[index] = value; } } } private void BuildStrongerCandidateList(List<WeightedCandidate> weighted, EnemyProfile sourceProfile, GameObject source, Dictionary<string, EnemyTemplatePool> pools, int nonFilthCount, int targetTier) { foreach (EnemyTemplatePool value in pools.Values) { if (value != null && value.HasUsableTemplate && !(value.Key == sourceProfile.Key) && !(value.Key == "maurice")) { EnemyProfile profileByKey = EnemyCatalog.GetProfileByKey(value.Key); if (profileByKey.Allowed && profileByKey.Tier == targetTier && profileByKey.Tier > sourceProfile.Tier && (!value.IsLarge || CanUseLargeEnemyCandidate(value, sourceProfile, source, nonFilthCount))) { float weight = (value.IsLarge ? 0.5f : 1f); weighted.Add(new WeightedCandidate(new EnemySpawnCandidate(value), weight)); } } } } private int TryApplySafeRandomizer(List<PendingWaveEnemy> wave, HashSet<int> replacedIds, int nonFilthCount) { if (wave == null || wave.Count < 2) { return 0; } if (!_randomizerEnabled.Value) { return 0; } if (EcmSpawnedMarker.ActiveCount >= 96) { return 0; } float num = Mathf.Clamp01(_randomizerChancePercent.Value / 100f); int num2 = Mathf.Max(0, _maxRandomizerReplacementsPerWave.Value); if (num <= 0f || num2 <= 0) { return 0; } List<PendingWaveEnemy> list = CopyWave(wave); Shuffle(list); int num3 = 0; for (int i = 0; i < list.Count; i++) { if (num3 >= num2) { break; } PendingWaveEnemy pendingWaveEnemy = list[i]; if (pendingWaveEnemy != null && !replacedIds.Contains(pendingWaveEnemy.SourceId) && !(Random.value > num) && IsEntrySourceAlive(pendingWaveEnemy) && !IsMaurice(pendingWaveEnemy.Profile) && !ShouldAvoidCloningOrReplacingSource(pendingWaveEnemy.Source)) { EnemySpawnCandidate enemySpawnCandidate = ChooseRandomizerCandidate(pendingWaveEnemy.Profile, pendingWaveEnemy.Source, nonFilthCount); if (enemySpawnCandidate != null && SpawnReplacement(pendingWaveEnemy, enemySpawnCandidate, keepOriginalIfFails: true, "Randomizer")) { replacedIds.Add(pendingWaveEnemy.SourceId); num3++; } } } if (num3 > 0) { SpawnAntiSoftlockFilth(wave, _randomizerAntiSoftlockFilthCount.Value, "Randomizer"); } return num3; } private EnemySpawnCandidate ChooseRandomizerCandidate(EnemyProfile sourceProfile, GameObject source, int nonFilthCount) { Dictionary<string, EnemyTemplatePool> preferredReplacementPools = GetPreferredReplacementPools(); if (preferredReplacementPools.Count <= 1) { return null; } List<WeightedCandidate> list = new List<WeightedCandidate>(); foreach (EnemyTemplatePool value in preferredReplacementPools.Values) { if (value != null && value.HasUsableTemplate && !(value.Key == sourceProfile.Key) && !(value.Key == "maurice") && (!value.IsLarge || CanUseLargeEnemyCandidate(value, sourceProfile, source, nonFilthCount))) { float num = 1f; if (value.Tier <= sourceProfile.Tier) { num *= 0.75f; } if (value.Tier >= sourceProfile.Tier + 2) { num *= 1.15f; } if (value.IsLarge) { num *= 0.5f; } list.Add(new WeightedCandidate(new EnemySpawnCandidate(value), num)); } } return PickWeightedCandidate(list); } private int SpawnAntiSoftlockFilth(List<PendingWaveEnemy> wave, int requestedCount, string sourceName) { if (requestedCount <= 0 || wave == null || wave.Count < 3) { return 0; } if (EcmSpawnedMarker.ActiveCount >= 96) { return 0; } GameObject val = FindAntiSoftlockAnchor(wave); if ((Object)(object)val == (Object)null) { return 0; } GameObject val2 = FindFilthTemplate(wave); if ((Object)(object)val2 == (Object)null || !IsTemplateSafeToSpawn(val2, "filth")) { return 0; } EnemyProfile profileByKey = EnemyCatalog.GetProfileByKey("filth"); int num = 0; for (int i = 0; i < requestedCount; i++) { if (EcmSpawnedMarker.ActiveCount >= 96) { break; } if (SpawnAdditionalTemplate(val2, val, profileByKey, sourceName + " Anti-Softlock Filth", i)) { num++; } } if (_debugLogging.Value && num > 0) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ECM " + sourceName + " spawned anti-softlock Filth x" + num + ".")); } return num; } private GameObject FindAntiSoftlockAnchor(List<PendingWaveEnemy> wave) { for (int i = 0; i < wave.Count; i++) { PendingWaveEnemy pendingWaveEnemy = wave[i]; if (pendingWaveEnemy != null && (Object)(object)pendingWaveEnemy.Source != (Object)null && pendingWaveEnemy.Source.activeInHierarchy) { return pendingWaveEnemy.Source; } } for (int j = 0; j < wave.Count; j++) { PendingWaveEnemy pendingWaveEnemy2 = wave[j]; if (pendingWaveEnemy2 != null && (Object)(object)pendingWaveEnemy2.Source != (Object)null) { return pendingWaveEnemy2.Source; } } return null; } private GameObject FindFilthTemplate(List<PendingWaveEnemy> wave) { for (int i = 0; i < wave.Count; i++) { PendingWaveEnemy pendingWaveEnemy = wave[i]; if (pendingWaveEnemy != null && pendingWaveEnemy.Profile.Key == "filth" && (Object)(object)pendingWaveEnemy.Source != (Object)null && pendingWaveEnemy.Source.activeInHierarchy && IsTemplateSafeToSpawn(pendingWaveEnemy.Source, "filth")) { return pendingWaveEnemy.Source; } } if (_scenePools.TryGetValue("filth", out var value) && value != null && value.HasUsableTemplate) { return value.GetRandomLiveTemplate(); } if (_globalPools.TryGetValue("filth", out value) && value != null && value.HasUsableTemplate) { return value.GetRandomLiveTemplate(); } return null; } private bool SpawnAdditionalTemplate(GameObject template, GameObject anchor, EnemyProfile profile, string sourceName, int index) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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) if ((Object)(object)template == (Object)null || (Object)(object)anchor == (Object)null) { return false; } try { Vector3 val = FindNearbySpawnPosition(anchor.transform.position, index + 100); GameObject val2 = Object.Instantiate<GameObject>(template, val, anchor.transform.rotation, anchor.transform.parent); ((Object)val2).name = ((Object)template).name + " [ECM " + sourceName + "]"; val2.AddComponent<EcmSpawnedMarker>(); SanitizeClonedEnemyRuntimeState(val2); if (!val2.activeSelf) { val2.SetActive(true); } if (!IsSpawnedCloneValid(val2, profile.Key)) { Object.Destroy((Object)(object)val2); return false; } ApplyConfiguredEnemyEffects(val2, isModdedEnemy: true); return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ECM skipped anti-softlock Filth safely: " + ex.GetType().Name + ": " + ex.Message)); return false; } } private EnemySpawnCandidate PickWeightedCandidate(List<WeightedCandidate> weighted) { if (weighted == null || weighted.Count == 0) { return null; } float num = 0f; for (int i = 0; i < weighted.Count; i++) { num += Mathf.Max(0f, weighted[i].Weight); } if (num <= 0f) { return null; } float num2 = Random.value * num; for (int j = 0; j < weighted.Count; j++) { num2 -= weighted[j].Weight; if (num2 <= 0f) { return weighted[j].Candidate; } } return weighted[weighted.Count - 1].Candidate; } private bool CanUseLargeEnemyCandidate(EnemyTemplatePool pool, EnemyProfile sourceProfile, GameObject source, int nonFilthCount) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (pool == null || !pool.IsLarge) { return true; } if (sourceProfile.IsFlying) { return false; } if (nonFilthCount < 5) { return false; } if ((Object)(object)source == (Object)null) { return false; } if (!HasGroundBelow(source.transform.position)) { return false; } return true; } private bool HasGroundBelow(Vector3 position) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) float num = 10f; RaycastHit val = default(RaycastHit); return Physics.Raycast(position + Vector3.up * 1.25f, Vector3.down, ref val, num + 1.25f, -1, (QueryTriggerInteraction)1); } private int TryApplyImmediateMitosis(GameObject source, EnemyProfile profile) { if (!_mitosisEnabled.Value) { return 0; } if ((Object)(object)source == (Object)null || !source.activeInHierarchy) { return 0; } if (EcmSpawnedMarker.ActiveCount >= 96) { return 0; } if (ShouldAvoidCloningOrReplacingSource(source)) { return 0; } float num = Mathf.Max(1f, _mitosisMultiplier.Value) - 1f; int num2 = Mathf.FloorToInt(num); if (Random.value < num - (float)num2) { num2++; } num2 = Mathf.Max(0, num2); if (num2 <= 0) { return 0; } int num3 = 0; for (int i = 0; i < num2; i++) { if (EcmSpawnedMarker.ActiveCount >= 96) { break; } if (SpawnDuplicate(source, profile, i)) { num3++; } } if (_debugLogging.Value && num3 > 0) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ECM Mitosis duplicated " + profile.Key + " x" + num3 + ".")); } return num3; } private bool SpawnReplacement(PendingWaveEnemy entry, EnemySpawnCandidate candidate, bool keepOriginalIfFails, string sourceName) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if (entry == null || candidate == null || candidate.Pool == null) { return false; } GameObject source = entry.Source; GameObject randomLiveTemplate = candidate.Pool.GetRandomLiveTemplate(); if ((Object)(object)source == (Object)null || (Object)(object)randomLiveTemplate == (Object)null) { return false; } if (!IsTemplateSafeToSpawn(randomLiveTemplate, candidate.Pool.Key)) { return false; } try { GameObject val = Object.Instantiate<GameObject>(randomLiveTemplate, source.transform.position, source.transform.rotation, source.transform.parent); ((Object)val).name = ((Object)randomLiveTemplate).name + " [ECM " + sourceName + "]"; val.AddComponent<EcmSpawnedMarker>(); SanitizeClonedEnemyRuntimeState(val); if (!val.activeSelf) { val.SetActive(true); } if (!IsSpawnedCloneValid(val, candidate.Pool.Key)) { Object.Destroy((Object)(object)val); return false; } ApplyConfiguredEnemyEffects(val, isModdedEnemy: true); if (keepOriginalIfFails) { ((MonoBehaviour)this).StartCoroutine(DestroyOriginalAfterReplacementValid(source, val, candidate.Pool.Key)); } else { Object.Destroy((Object)(object)source); } if (_debugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ECM " + sourceName + " replaced " + entry.Profile.Key + " with " + candidate.Pool.Key + ".")); } return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ECM skipped replacement safely: " + ex.GetType().Name + ": " + ex.Message)); return false; } } [IteratorStateMachine(typeof(<DestroyOriginalAfterReplacementValid>d__120))] private IEnumerator DestroyOriginalAfterReplacementValid(GameObject source, GameObject clone, string key) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DestroyOriginalAfterReplacementValid>d__120(0) { <>4__this = this, source = source, clone = clone, key = key }; } private bool SpawnDuplicate(GameObject source, EnemyProfile profile, int index) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null) { return false; } try { Vector3 val = FindNearbySpawnPosition(source.transform.position, index); GameObject val2 = Object.Instantiate<GameObject>(source, val, source.transform.rotation, source.transform.parent); ((Object)val2).name = ((Object)source).name + " [ECM Mitosis]"; val2.AddComponent<EcmSpawnedMarker>(); SanitizeClonedEnemyRuntimeState(val2); if (!val2.activeSelf) { val2.SetActive(true); } if (!IsSpawnedCloneValid(val2, profile.Key)) { Object.Destroy((Object)(object)val2); return false; } ApplyConfiguredEnemyEffects(val2, isModdedEnemy: true); return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ECM skipped duplicate safely: " + ex.GetType().Name + ": " + ex.Message)); return false; } } private Vector3 FindNearbySpawnPosition(Vector3 origin, int index) { //IL_0063: 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) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(0.25f, _spawnDistanceMin.Value); float num2 = Mathf.Max(num, _spawnDistanceMax.Value); float num3 = Random.Range(0f, MathF.PI * 2f) + (float)index * 2.399963f; float num4 = Random.Range(num, num2); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(Mathf.Cos(num3) * num4, 0f, Mathf.Sin(num3) * num4); Vector3 val2 = origin + val; RaycastHit val3 = default(RaycastHit); if (Physics.Raycast(val2 + Vector3.up * 4f, Vector3.down, ref val3, 12f, -1, (QueryTriggerInteraction)1)) { val2 = ((RaycastHit)(ref val3)).point + Vector3.up * 0.1f; } return val2; } private bool ApplyConfiguredEnemyEffects(GameObject enemy, bool isModdedEnemy) { if ((Object)(object)enemy == (Object)null || !NeedsEnemyEffectModifiers()) { return false; } bool result = false; if (_sandifyEnabled.Value && Random.value < Mathf.Clamp01(_sandifyChancePercent.Value / 100f)) { ApplySandify(enemy); result = true; } if (_radianceEnabled.Value && Random.value < Mathf.Clamp01(_radianceChancePercent.Value / 100f)) { ApplyRadiant(enemy); result = true; } if (_tankifyEnabled.Value && Random.value < Mathf.Clamp01(_tankifyChancePercent.Value / 100f)) { ApplyTankify(enemy); result = true; } if (_enrageEnabled.Value && Random.value < Mathf.Clamp01(_enrageChancePercent.Value / 100f)) { ApplyEnrage(enemy); result = true; } return result; } private void ApplySandify(GameObject enemy) { Component val = FindEnemyIdentifierComponent(enemy); if (!((Object)(object)val == (Object)null)) { Type type = ((object)val).GetType(); SetBoolMember(type, val, "sandified", value: true, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); InvokeMethodIfExists(type, val, "UpdateBuffs", null); InvokeMethodIfExists(type, val, "UpdateModifiers", null); } } private void ApplyTankify(GameObject enemy) { Component val = FindEnemyIdentifierComponent(enemy); if (!((Object)(object)val == (Object)null)) { Type type = ((object)val).GetType(); float num = Mathf.Clamp(_tankifyHealthMultiplier.Value, 0.05f, 20f); bool num2 = ReadBoolMember(type, val, "healthBuff", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); float num3 = Mathf.Max(0.01f, ReadFloatMember(type, val, "healthBuffModifier", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, 1f)); float num4 = (num2 ? (num3 * num) : num); num4 = Mathf.Clamp(num4, 0.01f, 100f); SetBoolMember(type, val, "healthBuff", value: true, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetFloatMember(type, val, "healthBuffModifier", num4, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); InvokeMethodIfExists(type, val, "UpdateBuffs", null); InvokeMethodIfExists(type, val, "UpdateModifiers", null); float num5 = ReadFloatMember(type, val, "totalHealthModifier", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, 1f); if (Mathf.Abs(num5 - num4) > 0.01f && num5 <= 1.01f && num4 > 1.01f) { SetFloatMember(type, val, "totalHealthModifier", num4, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } InvokeMethodIfExists(type, val, "ForceGetHealth", null); } } private void ApplyRadiant(GameObject enemy) { Component val = FindEnemyIdentifierComponent(enemy); if (!((Object)(object)val == (Object)null)) { Type type = ((object)val).GetType(); float value = Mathf.Clamp(_radianceTier.Value, 0.05f, 10f); bool value2 = _radianceAffectsDamage.Value; RadiancePreset radiancePreset = GetRadiancePreset(enemy, val); float value3 = radiancePreset.Speed; float value4 = radiancePreset.Health; float num = radiancePreset.Damage; if (_radianceUseCustomMultipliers.Value) { value3 = Mathf.Clamp(_radianceSpeedMultiplier.Value, 0.05f, 20f); value4 = Mathf.Clamp(_radianceHealthMultiplier.Value, 0.05f, 20f); num = Mathf.Clamp(_radianceDamageMultiplier.Value, 0.05f, 20f); } EnableCheatsIfPossible(); SetFloatMember(type, val, "radianceTier", value, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetFloatMember(type, val, "speedBuffModifier", value3, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetFloatMember(type, val, "healthBuffModifier", value4, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetFloatMember(type, val, "damageBuffModifier", value2 ? num : 1f, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetBoolMember(type, val, "speedBuff", value: true, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetBoolMember(type, val, "healthBuff", value: true, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetBoolMember(type, val, "damageBuff", value2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); InvokeMethodIfExists(type, val, "BuffAll", null); SetFloatMember(type, val, "radianceTier", value, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetFloatMember(type, val, "speedBuffModifier", value3, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetFloatMember(type, val, "healthBuffModifier", value4, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetFloatMember(type, val, "damageBuffModifier", value2 ? num : 1f, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetBoolMember(type, val, "speedBuff", value: true, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetBoolMember(type, val, "healthBuff", value: true, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetBoolMember(type, val, "damageBuff", value2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); InvokeMethodIfExists(type, val, "UpdateBuffs", null); InvokeMethodIfExists(type, val, "UpdateModifiers", null); InvokeMethodIfExists(type, val, "ForceGetHealth", null); } } private RadiancePreset GetRadiancePreset(GameObject enemy, Component enemyIdentifier) { return RadiancePreset.ForEnemyKey(EnemyCatalog.GetProfile(ReadEnemyTypeName(enemyIdentifier, enemy), ((Object)(object)enemy != (Object)null) ? ((Object)enemy).name : string.Empty, allowUnknown: true, allowExperimentalLarge: true).Key); } private void EnableCheatsIfPossible() { try { Type type = AccessTools.TypeByName("AssistController"); Component val = FindSingleComponent(type); if ((Object)(object)val != (Object)null) { SetBoolMember(type, val, "cheatsEnabled", value: true, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } } catch { } } private void ApplyEnrage(GameObject enemy) { Component[] componentsInChildren = enemy.GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { Type type = ((object)val).GetType(); if (CanEnrage(type) && !ReadBoolMember(type, val, "isEnraged", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { InvokeMethodIfExists(type, val, "Enrage", null); } } } } private bool CanEnrage(Type type) { if (type == null || GetCachedMethod(type, "Enrage") == null) { return false; } if (type.Name.IndexOf("enrage", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } Type[] interfaces = type.GetInterfaces(); for (int i = 0; i < interfaces.Length; i++) { string name = interfaces[i].Name; if (name.IndexOf("IEnrage", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Enrage", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } private void UpdateUltraHot() { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) if (!CanModifyCurrentScene() || _ultrahotEnabled == null || !_ultrahotEnabled.Value) { if (_ultrahotWasActive) { Time.timeScale = 1f; _ultrahotWasActive = false; } return; } try { if ((Object)(object)_cachedMovement == (Object)null || (Object)(object)_cachedMovementRigidbody == (Object)null) { _cachedMovement = FindSingleComponent(GetNewMovementType()); if ((Object)(object)_cachedMovement == (Object)null) { return; } _cachedMovementRigidbody = ReadRigidbodyMember(((object)_cachedMovement).GetType(), _cachedMovement, "rb"); } Rigidbody cachedMovementRigidbody = _cachedMovementRigidbody; if ((Object)(object)cachedMovementRigidbody == (Object)null) { _cachedMovement = null; _cachedMovementRigidbody = null; return; } float num = Mathf.Max(0.1f, _ultrahotVelocityForNormalTime.Value); Vector3 velocity = cachedMovementRigidbody.velocity; float num2 = ((Vector3)(ref velocity)).magnitude / num; num2 = Mathf.Clamp(num2, Mathf.Max(0.001f, _ultrahotMinimumTimeScale.Value), Mathf.Max(_ultrahotMinimumTimeScale.Value, _ultrahotMaximumTimeScale.Value)); Time.timeScale = Mathf.Lerp(Time.timeScale, num2, Time.unscaledDeltaTime * Mathf.Max(0.1f, _ultrahotSmoothing.Value)); _ultrahotWasActive = true; } catch (Exception ex) { if (_debugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ULTRAHOT update skipped safely: " + ex.Message)); } } } private void UpdateDualWieldMaintenance() { if (!CanModifyCurrentScene() || _dualWieldEnabled == null || !_dualWieldEnabled.Value || Time.time < _nextDualWieldRefresh) { return; } _nextDualWieldRefresh = Time.time + Mathf.Clamp(_dualWieldRefreshSeconds.Value, 0.05f, 10f); try { Type gunControlType = GetGunControlType(); Type dualWieldType = GetDualWieldType(); if (gunControlType == null || dualWieldType == null) { return; } Component val = FindSingleComponent(gunControlType); if (!((Object)(object)val == (Object)null)) { Component[] componentsInChildren = val.GetComponentsInChildren(dualWieldType, true); int num = Mathf.Clamp(_dualWieldStacks.Value, 0, 16); for (int i = ((componentsInChildren != null) ? componentsInChildren.Length : 0); i < num; i++) { AddDualWieldStack(val, dualWieldType, i); } } } catch (Exception ex) { if (_debugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Dual Wield maintenance skipped safely: " + ex.Message)); } } } private void AddDualWieldStack(Component gunControl, Type dualType, int existingCount) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("ECM Dual Wield"); val.transform.SetParent(gunControl.transform, true); val.transform.localRotation = Quaternion.identity; val.transform.localScale = (Vector3)((existingCount % 2 == 0) ? new Vector3(-1f, 1f, 1f) : Vector3.one); if (existingCount == 0) { val.transform.localPosition = Vector3.zero; } else if (existingCount % 2 == 0) { val.transform.localPosition = new Vector3((float)(existingCount / 2) * -1.5f, 0f, 0f); } else { val.transform.localPosition = new Vector3((float)((existingCount + 1) / 2) * 1.5f, 0f, 0f); } Component instance = val.AddComponent(dualType); SetFloatMember(dualType, instance, "delay", 0.05f + (float)existingCount / 20f, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); SetFloatMember(dualType, instance, "juiceAmount", Mathf.Max(1f, _dualWieldDuration.Value), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } private void RegisterTemplate(Dictionary<string, EnemyTemplatePool> pools, EnemyProfile profile, GameObject source, bool allowInactiveTemplates) { if (pools != null && !((Object)(object)source == (Object)null) && profile.Allowed && IsTemplateSafeToSpawn(source, profile.Key)) { if (!pools.TryGetValue(profile.Key, out var value)) { value = new EnemyTemplatePool(profile.Key, profile.Tier, profile.IsLarge, profile.IsFlying, allowInactiveTemplates); pools[profile.Key] = value; } value.AddTemplate(source); } } private bool IsTemplateSafeToSpawn(GameObject template, string key) { if ((Object)(object)template == (Object)null) { return false; } if ((Object)(object)template.GetComponent<EcmSpawnedMarker>() != (Object)null) { return false; } if (IsHardBlockedTemplateName((key ?? string.Empty) + " " + ((Object)template).name)) { return false; } if (!HasEnemyIdentifierComponent(template)) { return false; } return true; } private bool IsSpawnedCloneValid(GameObject clone, string key) { if ((Object)(object)clone == (Object)null || !clone.activeInHierarchy) { return false; } if (IsHardBlockedTemplateName((key ?? string.Empty) + " " + ((Object)clone).name)) { return false; } if (!HasEnemyIdentifierComponent(clone)) { return false; } return true; } private bool ShouldAvoidCloningOrReplacingSource(GameObject source) { if ((Object)(object)source == (Object)null) { return true; } try { List<Component> list = FindEnemyIdentifierComponents(source); for (int i = 0; i < list.Count; i++) { Component val = list[i]; if ((Object)(object)val == (Object)null) { continue; } Type type = ((object)val).GetType(); if (ReadBoolMember(type, val, "blessed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { if (_debugLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("ECM skipped Idol-blessed source: " + ((Object)source).name)); } return true; } } } catch { } return false; } private void SanitizeClonedEnemyRuntimeState(GameObject clone) { if ((Object)(object)clone == (Object)null) { return; } List<Component> list = FindEnemyIdentifierComponents(clone); for (int i = 0; i < list.Count; i++) { Component val = list[i]; if (!((Object)(object)val == (Object)null)) { Type type = ((object)val).GetType(); SetBoolMember(type, val, "blessed", value: false, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); InvokeMethodIfExists(type, val, "UpdateBuffs", null); InvokeMethodIfExists(type, val, "UpdateModifiers", null); } } } private bool IsEntrySourceAlive(PendingWaveEnemy entry) { if (entry != null && (Object)(object)entry.Source != (Object)null) { return entry.Source.activeInHierarchy; } return false; } private int CountNonFilthEnemies(List<PendingWaveEnemy> wave) { int num = 0; for (int i = 0; i < wave.Count; i++) { PendingWaveEnemy pendingWaveEnemy = wave[i]; if (pendingWaveEnemy != null && pendingWaveEnemy.Profile.Key != "filth") { num++; } } return num; } private List<PendingWaveEnemy> CopyWave(List<PendingWaveEnemy> wave) { List<PendingWaveEnemy> list = new List<PendingWaveEnemy>(); if (wave == null) { return list; } for (int i = 0; i < wave.Count; i++) { if (wave[i] != null) { list.Add(wave[i]); } } return list; } private void Shuffle<T>(List<T> list) { for (int i = 0; i < list.Count; i++) { int index = Random.Range(i, list.Count); T value = list[i]; list[i] = list[index]; list[index] = value; } } private bool IsMaurice(EnemyProfile profile) { if (!(profile.Key == "maurice")) { return Normalize(profile.Key).Contains("maliciousface"); } return true; } private bool IsHardBlockedTemplateName(string name) { string text = Normalize(name); if (!text.Contains("verycancerous") && !text.Contains("cancerousrodent") && !text.Contains("rodent") && !text.Contains("idol") && !text.Contains("deathcatcher") && !text.Contains("bigjohn") && !text.Contains("jakito") && !text.Contains("somethingwicked") && !text.Contains("prime") && !text.Contains("gabriel") && !text.Contains("fleshprison") && !text.Contains("fleshpanopticon") && !text.Contains("leviathan") && !text.Contains("earthmover")) { return text.Contains("v2"); } return true; } private Type GetEnemyIdentifierType() { if (_enemyIdentifierType == null) { _enemyIdentifierType = AccessTools.TypeByName("EnemyIdentifier"); } return _enemyIdentifierType; } private Type GetGunControlType() { if (_gunControlType == null) { _gunControlType = AccessTools.TypeByName("GunControl"); } return _gunControlType; } private Type GetDualWieldType() { if (_dualWieldType == null) { _dualWieldType = AccessTools.TypeByName("DualWield"); } return _dualWieldType; } private Type GetNewMovementType() { if (_newMovementType == null) { _newMovementType = AccessTools.TypeByName("NewMovement"); } return _newMovementType; } private Component FindSingleComponent(Type type) { if (type == null) { return null; } Object obj = Object.FindObjectOfType(type); return (Component)(object)((obj is Component) ? obj : null); } private bool HasEnemyIdentifierComponent(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return false; } Type enemyIdentifierType = GetEnemyIdentifierType(); if (enemyIdentifierType == null) { return false; } if (!((Object)(object)gameObject.GetComponent(enemyIdentifierType) != (Object)null)) { return (Object)(object)gameObject.GetComponentInChildren(enemyIdentifierType, true) != (Object)null; } return true; } private Component FindEnemyIdentifierComponent(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return null; } Type enemyIdentifierType = GetEnemyIdentifierType(); if (enemyIdentifierType == null) { return null; } return gameObject.GetComponent(enemyIdentifierType) ?? gameObject.GetComponentInChildren(enemyIdentifierType, true); } private List<Component> FindEnemyIdentifierComponents(GameObject gameObject) { List<Component> list = new List<Component>(); if ((Object)(object)gameObject == (Object)null) { return list; } Type enemyIdentifierType = GetEnemyIdentifierType(); if (enemyIdentifierType == null) { return list; } Component component = gameObject.GetComponent(enemyIdentifierType); if ((Object)(object)component != (Object)null) { list.Add(component); } Component[] componentsInChildren = gameObject.GetComponentsInChildren(enemyIdentifierType, true); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] != (Object)null && !list.Contains(componentsInChildren[i])) { list.Add(componentsInChildren[i]); } } return list; } private string ReadEnemyTypeName(object enemyObject, GameObject source) { if ((Object)(object)source != (Object)null) { string text = NormalizeObjectName(((Object)source).name); if (!string.IsNullOrEmpty(text)) { return text; } } if (enemyObject != null) { Type type = enemyObject.GetType(); string[] array = new string[6] { "enemyType", "EnemyType", "type", "enemyName", "FullName", "fullName" }; for (int i = 0; i < array.Length; i++) { string text2 = ReadStringMember(enemyObject, type, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, array[i]); if (!string.IsNullOrEmpty(text2)) { return text2; } } } if (!((Object)(object)source != (Object)null)) { return string.Empty; } return ((Object)source).name; } private string NormalizeObjectName(string name) { string text = Normalize(name); if (text.Contains("maliciousface") || text.Contains("maurice")) { return "maurice"; } if (text.Contains("hideousmass")) { return "hideousmass"; } if (text.Contains("sisyph") || text.Contains("insurrection")) { return "insurrectionist"; } if (text.Contains("streetclean")) { return "streetcleaner"; } if (text.Contains("guttertank")) { return "guttertank"; } if (text.Contains("gutterman")) { return "gutterman"; } if (text.Contains("mindflayer")) { return "mindflayer"; } if (text.Contains("swordsmachine")) { return "swordsmachine"; } if (text.Contains("cerberus")) { return "cerberus"; } if (text.Contains("ferryman")) { return "ferryman"; } if (text.Contains("providence")) { return "providence"; } if (text.Contains("sentry")) { return "sentry"; } if (text.Contains("virtue")) { return "virtue"; } if (text.Contains("drone")) { return "drone"; } if (text.Contains("soldier")) { return "soldier"; } if (text.Contains("schism")) {