// Consolidated decompiled source — Wubarrk-BlightedWorldHeart v1.0.6 // Generated by Hexium's decompiled-source browser. Best-effort concatenation of every type in this // version's manifest — decompiler output isn't guaranteed to compile as-is. using System; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis; using System.Collections.Generic; using HarmonyLib; using Jotunn; using UnityEngine; using System.Collections; using Jotunn.Managers; using System.Linq; using Jotunn.Configs; using Jotunn.Entities; using UnityEngine.UI; using BepInEx.Configuration; using BepInEx; using BepInEx.Bootstrap; using Jotunn.Utils; // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: Microsoft.CodeAnalysis.EmbeddedAttribute ---- namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: System.Runtime.CompilerServices.RefSafetyRulesAttribute ---- 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; } } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: BlightedHeart.BlightedHeartLoot ---- namespace BlightedHeart { [HarmonyPatch(typeof(Character), "OnDeath")] public static class BlightedHeartLoot { public static void Prefix(Character __instance) { BlightedWorldHeartAI component = __instance.GetComponent(); if (component != null && __instance.IsOwner()) { Logger.LogInfo((object)"Blighted World Heart defeated! Dropping loot..."); DropLoot(component.CurrentBiome, __instance.transform.position + Vector3.up * 2f); } } private static void DropLoot(Heightmap.Biome biome, Vector3 dropPosition) { if (!BlightedConfigManager.BiomeConfigs.TryGetValue(biome, out var value)) { return; } float value2 = value.LootScaleFactor.Value; List list = new List(); if (BlightedItemsManager.BiomeWeaponDrops.TryGetValue(biome, out var value3)) { list.AddRange(value3); } if (BlightedItemsManager.BiomeArmorDrops.TryGetValue(biome, out var value4)) { list.AddRange(value4); } if (list.Count == 0) { return; } int num = Mathf.CeilToInt(2f * value2); for (int i = 0; i < num; i++) { string name = list[Random.Range(0, list.Count)]; GameObject gameObject = ZNetScene.instance.GetPrefab(name); if (gameObject == null) { gameObject = ObjectDB.instance.GetItemPrefab(name); } if (gameObject != null) { Vector3 position = dropPosition + Random.insideUnitSphere * 2f; Object.Instantiate(gameObject, position, Quaternion.identity); } } } } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: BlightedHeart.BlightedHeartSpawning ---- namespace BlightedHeart { [HarmonyPatch(typeof(OfferingBowl))] public static class BlightedHeartSpawning { [HarmonyPatch("Interact")] [HarmonyPrefix] public static bool InteractPrefix(OfferingBowl __instance, Humanoid user, bool hold, bool alt) { if (hold) { return true; } if (__instance.m_bossPrefab == null) { return true; } return true; } [HarmonyPatch("UseItem")] [HarmonyPrefix] public static bool UseItemPrefix(OfferingBowl __instance, Humanoid user, ItemDrop.ItemData item, ref bool __result) { if (__instance.m_bossPrefab == null) { return true; } Heightmap.Biome key = Heightmap.FindBiome(__instance.transform.position); if (!BlightedConfigManager.BiomeConfigs.TryGetValue(key, out var value)) { return true; } string value2 = value.AltarSummonItem.Value; int value3 = value.AltarSummonAmount.Value; if (item.m_dropPrefab.name == value2) { if (item.m_stack < value3) { user.Message(MessageHud.MessageType.Center, $"Need {value3} {item.m_shared.m_name}"); __result = true; return false; } user.GetInventory().RemoveItem(item, value3); user.Message(MessageHud.MessageType.Center, "The Blighted World Heart awakens!"); SpawnBlightedHeart(__instance, user); __result = true; return false; } return true; } private static void SpawnBlightedHeart(OfferingBowl bowl, Humanoid user) { bowl.StartCoroutine(SpawnBlightedHeartCoroutine(bowl, user)); } private static IEnumerator SpawnBlightedHeartCoroutine(OfferingBowl bowl, Humanoid user) { GameObject heartPrefab = PrefabManager.Instance.GetPrefab("BlightedWorldHeart"); if (heartPrefab == null) { Logger.LogError((object)"Failed to get BlightedWorldHeart prefab for spawning."); yield break; } Vector3 spawnPos = bowl.transform.position + Vector3.up * 1f; Heightmap.Biome spawnBiome = Heightmap.FindBiome(spawnPos); if (spawnBiome == Heightmap.Biome.Swamp && user != null) { Vector3 dirToUser = user.transform.position - bowl.transform.position; dirToUser.y = 0f; spawnPos += dirToUser.normalized * 12f; } if (ZoneSystem.instance.GetSolidHeight(spawnPos, out var solidHeight)) { spawnPos.y = solidHeight; } if (bowl.m_spawnBossStartEffects != null) { bowl.m_spawnBossStartEffects.Create(spawnPos, Quaternion.identity); } yield return new WaitForSeconds((bowl.m_spawnBossDelay > 0f) ? bowl.m_spawnBossDelay : 8f); GameObject spawnedBoss = Object.Instantiate(heartPrefab, spawnPos, Quaternion.identity); Heightmap.Biome currentBiome = Heightmap.FindBiome(spawnPos); BlightedWorldHeartAI ai = spawnedBoss.GetComponent(); if (ai != null) { ai.Initialize(currentBiome); } } } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: BlightedHeart.BlightedItemsManager ---- namespace BlightedHeart { public static class BlightedItemsManager { public static Dictionary> BiomeWeaponDrops = new Dictionary>(); public static Dictionary> BiomeArmorDrops = new Dictionary>(); private static Dictionary _deferredRecipeLookups = new Dictionary(); private static bool _recipesResolved = false; public static void Setup() { PrefabManager.OnVanillaPrefabsAvailable -= Setup; Logger.LogInfo((object)"Setting up Blighted custom items..."); CreateBiomeItems(Heightmap.Biome.Meadows, new string[3] { "Club", "SpearFlint", "Bow" }, new string[5] { "ArmorLeatherChest", "ArmorLeatherLegs", "HelmetLeather", "CapeDeerHide", "ShieldWood" }); CreateBiomeItems(Heightmap.Biome.BlackForest, new string[3] { "SwordBronze", "MaceBronze", "AxeBronze" }, new string[5] { "ArmorBronzeChest", "ArmorBronzeLegs", "HelmetBronze", "CapeTrollHide", "ShieldBronzeBuckler" }); CreateBiomeItems(Heightmap.Biome.Swamp, new string[3] { "SwordIron", "MaceIron", "Battleaxe" }, new string[4] { "ArmorIronChest", "ArmorIronLegs", "HelmetIron", "ShieldBanded" }); CreateBiomeItems(Heightmap.Biome.Mountain, new string[2] { "SwordSilver", "BowDraugrFang" }, new string[5] { "ArmorWolfChest", "ArmorWolfLegs", "HelmetDrake", "CapeWolf", "ShieldSilver" }); CreateBiomeItems(Heightmap.Biome.Plains, new string[3] { "SwordBlackmetal", "MaceNeedle", "AtgeirBlackmetal" }, new string[5] { "ArmorPaddedCuirass", "ArmorPaddedGreaves", "HelmetPadded", "CapeLox", "ShieldBlackmetal" }); CreateBiomeItems(Heightmap.Biome.Mistlands, new string[2] { "SwordMistwalker", "BowSpineSnap" }, new string[5] { "ArmorCarapaceChest", "ArmorCarapaceLegs", "HelmetCarapace", "CapeFeather", "ShieldCarapace" }); CreateBiomeItems(Heightmap.Biome.AshLands, new string[2] { "SwordFlametal", "MaceFlametal" }, new string[5] { "ArmorFlametalChest", "ArmorFlametalLegs", "HelmetFlametal", "CapeAsh", "ShieldFlametal" }); CreateBiomeItems(Heightmap.Biome.DeepNorth, new string[2] { "SwordSilver", "BowDraugrFang" }, new string[5] { "ArmorWolfChest", "ArmorWolfLegs", "HelmetDrake", "CapeWolf", "ShieldSilver" }); Logger.LogInfo((object)"Blighted custom items created."); } private static void CreateBiomeItems(Heightmap.Biome biome, string[] weapons, string[] armors) { float scale = 1f; if (BlightedConfigManager.BiomeConfigs.TryGetValue(biome, out var value)) { scale = value.LootScaleFactor.Value; } BiomeWeaponDrops[biome] = new List(); BiomeArmorDrops[biome] = new List(); foreach (string basePrefab in weapons) { string text = CreateWeaponVariant(biome, basePrefab, "Serpent", "of the Serpent", "Attacks deal additional poison damage.", scale, delegate(ItemDrop.ItemData.SharedData shared) { shared.m_damages.m_poison += 10f * scale; }); if (text != null) { BiomeWeaponDrops[biome].Add(text); } string text2 = CreateWeaponVariant(biome, basePrefab, "Wraith", "of the Wraith", "Attacks deal additional spirit damage.", scale, delegate(ItemDrop.ItemData.SharedData shared) { shared.m_damages.m_spirit += 10f * scale; }); if (text2 != null) { BiomeWeaponDrops[biome].Add(text2); } string text3 = CreateWeaponVariant(biome, basePrefab, "Blight", "of the Blight", "Attacks deal increased physical, poison, and spirit damage.", scale, delegate(ItemDrop.ItemData.SharedData shared) { shared.m_damages.m_poison += 5f * scale; shared.m_damages.m_spirit += 5f * scale; shared.m_damages.m_slash *= 1f + 0.05f * scale; shared.m_damages.m_blunt *= 1f + 0.05f * scale; shared.m_damages.m_pierce *= 1f + 0.05f * scale; }); if (text3 != null) { BiomeWeaponDrops[biome].Add(text3); } } foreach (string basePrefab2 in armors) { string text4 = CreateArmorVariant(biome, basePrefab2, "Bastion", "of the Bastion", "Increased armor rating and durability.", scale, delegate(ItemDrop.ItemData.SharedData shared) { shared.m_armor *= 1f + 0.1f * scale; shared.m_maxDurability *= 1.5f; }); if (text4 != null) { BiomeArmorDrops[biome].Add(text4); } string text5 = CreateArmorVariant(biome, basePrefab2, "Serpent", "of the Serpent", "Grants resistance to Poison.", scale, delegate(ItemDrop.ItemData.SharedData shared) { if (shared.m_damageModifiers == null) { shared.m_damageModifiers = new List(); } shared.m_damageModifiers.Add(new HitData.DamageModPair { m_type = HitData.DamageType.Poison, m_modifier = HitData.DamageModifier.Resistant }); }); if (text5 != null) { BiomeArmorDrops[biome].Add(text5); } string text6 = CreateArmorVariant(biome, basePrefab2, "Wraith", "of the Wraith", "Grants resistance to Spirit damage.", scale, delegate(ItemDrop.ItemData.SharedData shared) { if (shared.m_damageModifiers == null) { shared.m_damageModifiers = new List(); } shared.m_damageModifiers.Add(new HitData.DamageModPair { m_type = HitData.DamageType.Spirit, m_modifier = HitData.DamageModifier.Resistant }); }); if (text6 != null) { BiomeArmorDrops[biome].Add(text6); } string text7 = CreateArmorVariant(biome, basePrefab2, "Blight", "of the Blight", "Increased armor and resistance to Poison and Spirit.", scale, delegate(ItemDrop.ItemData.SharedData shared) { shared.m_armor *= 1f + 0.05f * scale; if (shared.m_damageModifiers == null) { shared.m_damageModifiers = new List(); } shared.m_damageModifiers.Add(new HitData.DamageModPair { m_type = HitData.DamageType.Poison, m_modifier = HitData.DamageModifier.Resistant }); shared.m_damageModifiers.Add(new HitData.DamageModPair { m_type = HitData.DamageType.Spirit, m_modifier = HitData.DamageModifier.Resistant }); }); if (text7 != null) { BiomeArmorDrops[biome].Add(text7); } } } private static string GetStationName(string basePrefab) { if (basePrefab.Contains("Carapace") || basePrefab.Contains("Flametal") || basePrefab.Contains("Mistwalker") || basePrefab.Contains("SpineSnap") || basePrefab.Contains("Ash") || basePrefab.Contains("Feather")) { return "blackforge"; } if (basePrefab.Contains("Bronze") || basePrefab.Contains("Iron") || basePrefab.Contains("Silver") || basePrefab.Contains("Blackmetal") || basePrefab.Contains("Banded") || basePrefab.Contains("Padded") || basePrefab.Contains("Needle") || basePrefab.Contains("DraugrFang") || basePrefab.Contains("Battleaxe") || basePrefab.Contains("Drake")) { return "forge"; } return "piece_workbench"; } private static string[] GetBiomeTrophies(Heightmap.Biome biome) { return biome switch { Heightmap.Biome.Meadows => new string[2] { "TrophyBoar", "TrophyNeck" }, Heightmap.Biome.BlackForest => new string[2] { "TrophyGreydwarfBrute", "TrophyGreydwarfShaman" }, Heightmap.Biome.Swamp => new string[2] { "TrophyDraugr", "TrophyLeech" }, Heightmap.Biome.Mountain => new string[2] { "TrophyWolf", "TrophyHatchling" }, Heightmap.Biome.Plains => new string[2] { "TrophyGoblin", "TrophyDeathsquito" }, Heightmap.Biome.Mistlands => new string[2] { "TrophySeeker", "TrophyTick" }, Heightmap.Biome.AshLands => new string[2] { "TrophyCharredMelee", "TrophyCharredArcher" }, Heightmap.Biome.DeepNorth => new string[2] { "TrophyWolf", "TrophyHatchling" }, _ => new string[2] { "TrophyBoar", "TrophyNeck" }, }; } public static void ResolveVanillaRecipeMaterials() { if (_recipesResolved) { return; } _recipesResolved = true; if (BlightedHeartPlugin.IsWackyDBInstalled) { Logger.LogInfo((object)"WackyDB Recipe Compatibility mode is active. Skipping vanilla recipe material injection."); return; } if (ObjectDB.instance == null) { Logger.LogWarning((object)"ResolveVanillaRecipeMaterials called but ObjectDB.instance is null!"); return; } int num = 0; int num2 = 0; foreach (KeyValuePair deferredRecipeLookup in _deferredRecipeLookups) { string blightedName = deferredRecipeLookup.Key; string basePrefab = deferredRecipeLookup.Value; Recipe recipe = ObjectDB.instance.m_recipes.FirstOrDefault((Recipe r) => r != null && r.m_item != null && r.m_item.gameObject.name == blightedName); if (recipe == null) { continue; } Recipe recipe2 = ObjectDB.instance.m_recipes.FirstOrDefault((Recipe r) => r != null && r.m_item != null && r.m_item.gameObject.name == basePrefab); if (recipe2 == null || recipe2.m_resources == null) { Logger.LogWarning((object)("Could not find vanilla recipe for '" + basePrefab + "', upgrade for '" + blightedName + "' will only require trophies.")); num2++; continue; } List list = ((recipe.m_resources != null) ? new List(recipe.m_resources) : new List()); Piece.Requirement[] resources = recipe2.m_resources; foreach (Piece.Requirement requirement in resources) { if (!(requirement.m_resItem == null)) { int amountPerLevel = ((requirement.m_amountPerLevel > 0) ? requirement.m_amountPerLevel : Mathf.Max(1, requirement.m_amount / 2)); list.Add(new Piece.Requirement { m_resItem = requirement.m_resItem, m_amount = 0, m_amountPerLevel = amountPerLevel, m_recover = true }); } } recipe.m_resources = list.ToArray(); num++; } Logger.LogInfo((object)$"Resolved vanilla recipe materials for {num} blighted items ({num2} could not be resolved)."); } private static string CreateWeaponVariant(Heightmap.Biome biome, string basePrefab, string suffixId, string suffixName, string effectDescription, float scale, Action modify) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Expected O, but got Unknown //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown string text = $"Blighted{biome}_{basePrefab}_{suffixId}"; GameObject prefab = PrefabManager.Instance.GetPrefab(basePrefab); if (prefab == null) { return null; } CustomItem val = new CustomItem(text, basePrefab); ItemDrop.ItemData.SharedData shared = val.ItemDrop.m_itemData.m_shared; shared.m_name = "Blighted " + shared.m_name + " " + suffixName; shared.m_description = "" + effectDescription + "\n" + shared.m_description; shared.m_damages.m_blunt *= 1f + 0.05f * scale; shared.m_damages.m_slash *= 1f + 0.05f * scale; shared.m_damages.m_pierce *= 1f + 0.05f * scale; modify?.Invoke(shared); TintItemPrefab(val); ItemManager.Instance.AddItem(val); string[] biomeTrophies = GetBiomeTrophies(biome); List list = new List(); _deferredRecipeLookups[text] = basePrefab; string[] array = biomeTrophies; foreach (string item in array) { list.Add(new RequirementConfig { Item = item, Amount = 0, AmountPerLevel = 2 }); } CustomRecipe val2 = new CustomRecipe(new RecipeConfig { Item = text, Amount = 1, CraftingStation = GetStationName(basePrefab), Enabled = true, Requirements = list.ToArray() }); ItemManager.Instance.AddRecipe(val2); return text; } private static string CreateArmorVariant(Heightmap.Biome biome, string basePrefab, string suffixId, string suffixName, string effectDescription, float scale, Action modify) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown string text = $"Blighted{biome}_{basePrefab}_{suffixId}"; GameObject prefab = PrefabManager.Instance.GetPrefab(basePrefab); if (prefab == null) { return null; } CustomItem val = new CustomItem(text, basePrefab); ItemDrop.ItemData.SharedData shared = val.ItemDrop.m_itemData.m_shared; shared.m_name = "Blighted " + shared.m_name + " " + suffixName; shared.m_description = "" + effectDescription + "\n" + shared.m_description; shared.m_armor *= 1f + 0.05f * scale; modify?.Invoke(shared); TintItemPrefab(val); ItemManager.Instance.AddItem(val); string[] biomeTrophies = GetBiomeTrophies(biome); List list = new List(); _deferredRecipeLookups[text] = basePrefab; string[] array = biomeTrophies; foreach (string item in array) { list.Add(new RequirementConfig { Item = item, Amount = 0, AmountPerLevel = 2 }); } CustomRecipe val2 = new CustomRecipe(new RecipeConfig { Item = text, Amount = 1, CraftingStation = GetStationName(basePrefab), Enabled = true, Requirements = list.ToArray() }); ItemManager.Instance.AddRecipe(val2); return text; } private static void TintItemPrefab(CustomItem customItem) { Color blightedTint = new Color(0.1f, 0.4f, 0.1f, 1f); Renderer[] componentsInChildren = customItem.ItemPrefab.GetComponentsInChildren(includeInactive: true); foreach (Renderer renderer in componentsInChildren) { Material[] materials = renderer.materials; foreach (Material mat in materials) { TintMaterial(mat, blightedTint); } } ItemDrop.ItemData.SharedData shared = customItem.ItemDrop.m_itemData.m_shared; if (shared != null && shared.m_armorMaterial != null) { shared.m_armorMaterial = new Material(shared.m_armorMaterial); TintMaterial(shared.m_armorMaterial, blightedTint); } } private static void TintMaterial(Material mat, Color blightedTint) { if (!(mat == null)) { if (mat.HasProperty("_Color")) { mat.color = Color.Lerp(mat.color, blightedTint, 0.8f); } if (mat.HasProperty("_EmissionColor") && mat.IsKeywordEnabled("_EMISSION")) { mat.SetColor("_EmissionColor", mat.GetColor("_EmissionColor") * blightedTint); } } } } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: BlightedHeart.BlightedMechanicsPatches ---- namespace BlightedHeart { [HarmonyPatch] public static class BlightedMechanicsPatches { [HarmonyPatch(typeof(Player), "OnSpawned")] [HarmonyPostfix] public static void PlayerOnSpawnedPostfix() { BlightedItemsManager.ResolveVanillaRecipeMaterials(); } [HarmonyPatch(typeof(Character), "Damage")] [HarmonyPrefix] public static bool DamagePrefix(Character __instance, HitData hit) { if (__instance.GetComponent() != null && hit != null && (hit.GetAttacker() == null || !hit.GetAttacker().IsPlayer())) { return false; } if (hit == null || hit.GetAttacker() == null || !hit.GetAttacker().IsPlayer()) { return true; } return true; } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] [HarmonyPrefix] public static void HideBlightedCrafting(List recipes) { if (recipes != null && InventoryGui.instance != null && InventoryGui.instance.InCraftTab()) { recipes.RemoveAll((Recipe r) => r != null && r.m_item != null && r.m_item.gameObject.name.StartsWith("Blighted") && IsRecipeFreeAtLevel1(r)); } } private static bool IsRecipeFreeAtLevel1(Recipe r) { if (r.m_resources == null || r.m_resources.Length == 0) { return true; } Piece.Requirement[] resources = r.m_resources; foreach (Piece.Requirement requirement in resources) { if (requirement != null && requirement.m_amount > 0) { return false; } } return true; } [HarmonyPatch(typeof(Player), "AddKnownItem", new Type[] { typeof(ItemDrop.ItemData) })] [HarmonyPrefix] public static void AddKnownItemPrefix(Player __instance, ItemDrop.ItemData item) { if (item == null || item.m_dropPrefab == null || !item.m_dropPrefab.name.StartsWith("Blighted")) { return; } string prefabName = item.m_dropPrefab.name; if (ObjectDB.instance == null) { return; } Recipe recipe = ObjectDB.instance.m_recipes.FirstOrDefault((Recipe r) => r != null && r.m_item != null && r.m_item.gameObject.name == prefabName); if (recipe == null || recipe.m_resources == null) { return; } Piece.Requirement[] resources = recipe.m_resources; foreach (Piece.Requirement requirement in resources) { if (requirement.m_resItem != null) { __instance.m_knownMaterial.Add(requirement.m_resItem.gameObject.name); } } } } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: BlightedHeart.BlightedUIManager ---- namespace BlightedHeart { public class BlightedUIManager : MonoBehaviour { private static BlightedUIManager _instance; private GameObject _uiRoot; private BlightedWorldHeartAI _activeHeart; private Character _trackedSubBoss1; private Character _trackedSubBoss2; private Vector3 _heartPosition; private RectTransform _mainBossBar; private Image _mainBossFill; private Text _mainBossName; private RectTransform _subBoss1Bar; private Image _subBoss1Fill; private Text _subBoss1Name; private RectTransform _subBoss2Bar; private Image _subBoss2Fill; private Text _subBoss2Name; private Font _valheimFont; public static BlightedUIManager Instance => _instance; public void Awake() { _instance = this; } public void RegisterHeart(BlightedWorldHeartAI heart) { _activeHeart = heart; if (_activeHeart != null) { _heartPosition = _activeHeart.transform.position; } if (_uiRoot == null && Hud.instance != null) { CreateUI(); } } public void UnregisterHeart(BlightedWorldHeartAI heart) { if (_activeHeart == heart) { _activeHeart = null; } } private void CreateUI() { if (!(Hud.instance == null) && !(Hud.instance.m_rootObject == null)) { Text componentInChildren = Hud.instance.m_rootObject.GetComponentInChildren(includeInactive: true); if (componentInChildren != null) { _valheimFont = componentInChildren.font; } _uiRoot = new GameObject("BlightedCustomBossUI"); _uiRoot.transform.SetParent(Hud.instance.m_rootObject.transform, worldPositionStays: false); RectTransform rectTransform = _uiRoot.AddComponent(); rectTransform.anchorMin = new Vector2(0.5f, 1f); rectTransform.anchorMax = new Vector2(0.5f, 1f); rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.anchoredPosition = new Vector2(0f, -50f); CreateHealthBar("MainBossBar", _uiRoot.transform, new Vector2(0f, 0f), new Vector2(600f, 30f), out _mainBossBar, out _mainBossFill, out _mainBossName); CreateHealthBar("SubBoss1Bar", _uiRoot.transform, new Vector2(-200f, -50f), new Vector2(250f, 20f), out _subBoss1Bar, out _subBoss1Fill, out _subBoss1Name); CreateHealthBar("SubBoss2Bar", _uiRoot.transform, new Vector2(200f, -50f), new Vector2(250f, 20f), out _subBoss2Bar, out _subBoss2Fill, out _subBoss2Name); _uiRoot.SetActive(value: false); } } private void CreateHealthBar(string name, Transform parent, Vector2 anchoredPos, Vector2 size, out RectTransform rootRt, out Image fill, out Text nameText) { GameObject gameObject = new GameObject(name); gameObject.transform.SetParent(parent, worldPositionStays: false); rootRt = gameObject.AddComponent(); rootRt.anchorMin = new Vector2(0.5f, 1f); rootRt.anchorMax = new Vector2(0.5f, 1f); rootRt.pivot = new Vector2(0.5f, 1f); rootRt.anchoredPosition = anchoredPos; rootRt.sizeDelta = size; GameObject gameObject2 = new GameObject("Trim"); gameObject2.transform.SetParent(gameObject.transform, worldPositionStays: false); RectTransform rectTransform = gameObject2.AddComponent(); rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = new Vector2(-2f, -2f); rectTransform.offsetMax = new Vector2(2f, 2f); Image image = gameObject2.AddComponent(); image.color = new Color(1f, 0.84f, 0f, 1f); GameObject gameObject3 = new GameObject("Bkg"); gameObject3.transform.SetParent(gameObject.transform, worldPositionStays: false); RectTransform rectTransform2 = gameObject3.AddComponent(); rectTransform2.anchorMin = Vector2.zero; rectTransform2.anchorMax = Vector2.one; rectTransform2.sizeDelta = Vector2.zero; Image image2 = gameObject3.AddComponent(); image2.color = new Color(0f, 0f, 0f, 0.9f); GameObject gameObject4 = new GameObject("Fill"); gameObject4.transform.SetParent(gameObject.transform, worldPositionStays: false); RectTransform rectTransform3 = gameObject4.AddComponent(); rectTransform3.anchorMin = new Vector2(0f, 0f); rectTransform3.anchorMax = new Vector2(1f, 1f); rectTransform3.sizeDelta = Vector2.zero; rectTransform3.pivot = new Vector2(0f, 0.5f); fill = gameObject4.AddComponent(); fill.color = Color.magenta; GameObject gameObject5 = new GameObject("NameText"); gameObject5.transform.SetParent(gameObject.transform, worldPositionStays: false); RectTransform rectTransform4 = gameObject5.AddComponent(); rectTransform4.anchorMin = new Vector2(0.5f, 0.5f); rectTransform4.anchorMax = new Vector2(0.5f, 0.5f); rectTransform4.pivot = new Vector2(0.5f, 0.5f); rectTransform4.anchoredPosition = new Vector2(0f, 0f); rectTransform4.sizeDelta = new Vector2(500f, 30f); nameText = gameObject5.AddComponent(); nameText.alignment = TextAnchor.MiddleCenter; nameText.color = Color.white; nameText.fontSize = 20; if (_valheimFont != null) { nameText.font = _valheimFont; } else { nameText.font = Resources.GetBuiltinResource("Arial.ttf"); } Shadow shadow = gameObject5.AddComponent(); shadow.effectColor = Color.black; shadow.effectDistance = new Vector2(1f, -1f); } public void LateUpdate() { if (Hud.instance == null) { return; } if (_activeHeart != null && _activeHeart.m_health > 0f) { _heartPosition = _activeHeart.transform.position; if (_activeHeart.m_subBoss1 != null) { _trackedSubBoss1 = _activeHeart.m_subBoss1; } if (_activeHeart.m_subBoss2 != null) { _trackedSubBoss2 = _activeHeart.m_subBoss2; } } bool flag = _activeHeart != null && _activeHeart.m_health > 0f; bool flag2 = _trackedSubBoss1 != null && !_trackedSubBoss1.IsDead(); bool flag3 = _trackedSubBoss2 != null && !_trackedSubBoss2.IsDead(); if (!flag && !flag2 && !flag3) { if (_uiRoot != null && _uiRoot.activeSelf) { _uiRoot.SetActive(value: false); } return; } if (Player.m_localPlayer != null) { float num = Vector3.Distance(Player.m_localPlayer.transform.position, _heartPosition); if (num > 100f) { if (_uiRoot != null && _uiRoot.activeSelf) { _uiRoot.SetActive(value: false); } return; } } if (_uiRoot == null) { CreateUI(); } if (!(_uiRoot == null)) { if (!_uiRoot.activeSelf) { _uiRoot.SetActive(value: true); } UpdateBars(flag, flag2, flag3); } } private void UpdateBars(bool heartAlive, bool sb1Alive, bool sb2Alive) { if (heartAlive) { _mainBossBar.gameObject.SetActive(value: true); float health = _activeHeart.m_health; float maxHealth = _activeHeart.m_maxHealth; _mainBossFill.color = GetBiomeColor(_activeHeart.CurrentBiome); _mainBossName.text = "Blighted World Heart"; UpdateFill(_mainBossFill, health, maxHealth); } else { _mainBossBar.gameObject.SetActive(value: false); } float y = (heartAlive ? (-50f) : 0f); if (sb1Alive) { _subBoss1Bar.gameObject.SetActive(value: true); _subBoss1Name.text = GetLocalizedBlightedName(_trackedSubBoss1.m_name); _subBoss1Fill.color = new Color(0.2f, 0.6f, 1f, 1f); UpdateFill(_subBoss1Fill, _trackedSubBoss1.GetHealth(), _trackedSubBoss1.GetMaxHealth()); } else { _subBoss1Bar.gameObject.SetActive(value: false); } if (sb2Alive) { _subBoss2Bar.gameObject.SetActive(value: true); _subBoss2Name.text = GetLocalizedBlightedName(_trackedSubBoss2.m_name); _subBoss2Fill.color = new Color(0.2f, 0.6f, 1f, 1f); UpdateFill(_subBoss2Fill, _trackedSubBoss2.GetHealth(), _trackedSubBoss2.GetMaxHealth()); } else { _subBoss2Bar.gameObject.SetActive(value: false); } if (sb1Alive && sb2Alive) { _subBoss1Bar.sizeDelta = new Vector2(250f, 20f); _subBoss1Bar.anchoredPosition = new Vector2(-200f, y); _subBoss1Name.fontSize = 20; _subBoss2Bar.sizeDelta = new Vector2(250f, 20f); _subBoss2Bar.anchoredPosition = new Vector2(200f, y); _subBoss2Name.fontSize = 20; } else if (sb1Alive && !sb2Alive && !heartAlive) { _subBoss1Bar.sizeDelta = new Vector2(600f, 30f); _subBoss1Bar.anchoredPosition = new Vector2(0f, 0f); _subBoss1Name.fontSize = 24; } else if (!sb1Alive && sb2Alive && !heartAlive) { _subBoss2Bar.sizeDelta = new Vector2(600f, 30f); _subBoss2Bar.anchoredPosition = new Vector2(0f, 0f); _subBoss2Name.fontSize = 24; } else if (sb1Alive && !sb2Alive && heartAlive) { _subBoss1Bar.sizeDelta = new Vector2(250f, 20f); _subBoss1Bar.anchoredPosition = new Vector2(0f, y); _subBoss1Name.fontSize = 20; } else if (!sb1Alive && sb2Alive && heartAlive) { _subBoss2Bar.sizeDelta = new Vector2(250f, 20f); _subBoss2Bar.anchoredPosition = new Vector2(0f, y); _subBoss2Name.fontSize = 20; } } private void UpdateFill(Image fill, float current, float max) { float x = Mathf.Clamp01((max > 0f) ? (current / max) : 0f); fill.transform.localScale = new Vector3(x, 1f, 1f); } private string GetLocalizedBlightedName(string rawName) { if (string.IsNullOrEmpty(rawName)) { return "Blighted Unknown"; } if (rawName.StartsWith("Blighted")) { return rawName; } string text = Localization.instance.Localize(rawName); return "Blighted " + text; } private Color GetBiomeColor(Heightmap.Biome biome) { return biome switch { Heightmap.Biome.Meadows => new Color(0.2f, 0.8f, 0.2f), Heightmap.Biome.BlackForest => new Color(0.5f, 0.1f, 0.8f), Heightmap.Biome.Swamp => new Color(0.4f, 0.3f, 0.1f), Heightmap.Biome.Mountain => new Color(0.8f, 0.9f, 1f), Heightmap.Biome.Plains => new Color(0.9f, 0.7f, 0.2f), Heightmap.Biome.Mistlands => new Color(0.3f, 0.1f, 0.5f), Heightmap.Biome.AshLands => new Color(1f, 0.2f, 0f), Heightmap.Biome.DeepNorth => new Color(0.9f, 0.9f, 1f), _ => Color.white, }; } } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: BlightedHeart.BlightedWorldHeartAI ---- namespace BlightedHeart { public class BlightedWorldHeartAI : MonoBehaviour, IDestructible { private static readonly int RPC_BlightedDamage = "RPC_BlightedHeartDamage".GetStableHashCode(); public float m_health; public float m_maxHealth = 2000f; private bool m_isDead = false; public Heightmap.Biome CurrentBiome; private float _lastWaveTime; private float _waveInterval = 30f; private bool _subBoss1Spawned = false; private bool _subBoss2Spawned = false; public Character m_subBoss1; public Character m_subBoss2; public ZNetView m_nview; private float _lastLogTime = 0f; private static readonly int ZDO_Health = "blighted_health".GetStableHashCode(); private static readonly int ZDO_MaxHealth = "blighted_max_health".GetStableHashCode(); private static readonly string ZDO_SubBoss1 = "blighted_subboss1"; private static readonly string ZDO_SubBoss2 = "blighted_subboss2"; private List m_spawnedEnemies = new List(); public void Awake() { m_nview = GetComponent(); CharacterAnimEvent[] componentsInChildren = GetComponentsInChildren(includeInactive: true); foreach (CharacterAnimEvent obj in componentsInChildren) { Object.DestroyImmediate(obj); } CharacterDrop[] componentsInChildren2 = GetComponentsInChildren(includeInactive: true); foreach (CharacterDrop obj2 in componentsInChildren2) { Object.DestroyImmediate(obj2); } BaseAI[] componentsInChildren3 = GetComponentsInChildren(includeInactive: true); foreach (BaseAI obj3 in componentsInChildren3) { Object.DestroyImmediate(obj3); } FootStep[] componentsInChildren4 = GetComponentsInChildren(includeInactive: true); foreach (FootStep obj4 in componentsInChildren4) { Object.DestroyImmediate(obj4); } VisEquipment[] componentsInChildren5 = GetComponentsInChildren(includeInactive: true); foreach (VisEquipment obj5 in componentsInChildren5) { Object.DestroyImmediate(obj5); } Character[] componentsInChildren6 = GetComponentsInChildren(includeInactive: true); foreach (Character character in componentsInChildren6) { Logger.LogInfo((object)("[BlightedHeart] Stripping Character: " + character.gameObject.name)); Object.DestroyImmediate(character); } ZSyncAnimation[] componentsInChildren7 = GetComponentsInChildren(includeInactive: true); foreach (ZSyncAnimation obj6 in componentsInChildren7) { Object.DestroyImmediate(obj6); } Animator[] componentsInChildren8 = GetComponentsInChildren(includeInactive: true); foreach (Animator obj7 in componentsInChildren8) { Object.DestroyImmediate(obj7); } Rigidbody[] componentsInChildren9 = GetComponentsInChildren(includeInactive: true); foreach (Rigidbody obj8 in componentsInChildren9) { Object.DestroyImmediate(obj8); } Aoe[] componentsInChildren10 = GetComponentsInChildren(includeInactive: true); foreach (Aoe aoe in componentsInChildren10) { Logger.LogInfo((object)("[BlightedHeart] Stripping Aoe: " + aoe.gameObject.name)); Object.DestroyImmediate(aoe); } MineRock5[] componentsInChildren11 = GetComponentsInChildren(includeInactive: true); foreach (MineRock5 obj9 in componentsInChildren11) { Object.DestroyImmediate(obj9); } HitArea[] componentsInChildren12 = GetComponentsInChildren(includeInactive: true); foreach (HitArea obj10 in componentsInChildren12) { Object.DestroyImmediate(obj10); } Destructible[] componentsInChildren13 = GetComponentsInChildren(includeInactive: true); foreach (Destructible obj11 in componentsInChildren13) { Object.DestroyImmediate(obj11); } Collider[] componentsInChildren14 = GetComponentsInChildren(includeInactive: true); foreach (Collider collider in componentsInChildren14) { if (collider != null && collider.isTrigger) { Object.DestroyImmediate(collider); } } MonoBehaviour[] componentsInChildren15 = GetComponentsInChildren(includeInactive: true); foreach (MonoBehaviour monoBehaviour in componentsInChildren15) { if (!(monoBehaviour == null) && !(monoBehaviour is ZNetView) && !(monoBehaviour is BlightedWorldHeartAI) && !(monoBehaviour is BlightedDamageForwarder)) { Object.DestroyImmediate(monoBehaviour); } } if (m_nview != null) { m_nview.Register("RPC_BlightedHeartDamage", RPC_HandleDamage); } m_health = m_maxHealth; if (m_nview != null && m_nview.GetZDO() != null) { float num10 = m_nview.GetZDO().GetFloat(ZDO_Health); if (num10 > 0f) { m_health = num10; } float num11 = m_nview.GetZDO().GetFloat(ZDO_MaxHealth); if (num11 > 0f) { m_maxHealth = num11; } } Collider[] componentsInChildren16 = GetComponentsInChildren(includeInactive: true); foreach (Collider collider2 in componentsInChildren16) { if (collider2 != null && collider2.gameObject != base.gameObject) { BlightedDamageForwarder blightedDamageForwarder = collider2.gameObject.GetComponent(); if (blightedDamageForwarder == null) { blightedDamageForwarder = collider2.gameObject.AddComponent(); } blightedDamageForwarder.m_heart = this; } } Collider component = base.gameObject.GetComponent(); if (component == null) { BoxCollider boxCollider = base.gameObject.AddComponent(); boxCollider.size = new Vector3(10f, 10f, 10f); boxCollider.center = Vector3.zero; } if (BlightedUIManager.Instance != null) { BlightedUIManager.Instance.RegisterHeart(this); } Logger.LogInfo((object)"[BlightedHeart] Runtime cleanup complete. Custom IDestructible active."); } public DestructibleType GetDestructibleType() { return DestructibleType.Default; } public void Damage(HitData hit) { if (m_isDead || m_nview == null || !m_nview.IsValid()) { return; } if (hit != null) { Character attacker = hit.GetAttacker(); if (attacker == null || (!attacker.IsPlayer() && !attacker.IsTamed())) { return; } } if (!m_nview.IsOwner()) { m_nview.InvokeRPC("RPC_BlightedHeartDamage", hit); return; } float totalDamage = hit.GetTotalDamage(); if (!(totalDamage <= 0f)) { m_health -= totalDamage; m_nview.GetZDO().Set(ZDO_Health, m_health); if (DamageText.instance != null) { DamageText.instance.ShowText(DamageText.TextType.Normal, hit.m_point, totalDamage); } if (BlightedConfigManager.DebugMode != null && BlightedConfigManager.DebugMode.Value) { Logger.LogInfo((object)$"[BlightedHeart] Took {totalDamage:F1} damage. Health: {m_health:F0}/{m_maxHealth:F0}"); } if (m_health <= 0f) { m_health = 0f; m_isDead = true; OnDeath(); } } } private void RPC_HandleDamage(long sender, HitData hit) { Damage(hit); } private void OnDeath() { Logger.LogInfo((object)"[BlightedHeart] The Blighted World Heart has been destroyed!"); DropLoot(); if (Player.m_localPlayer != null) { Player.MessageAllInRange(base.transform.position, 200f, MessageHud.MessageType.Center, "The Blighted World Heart has been purified!"); } if (m_nview != null && m_nview.IsOwner()) { m_nview.Destroy(); } } private void DropLoot() { if (!BlightedConfigManager.BiomeConfigs.TryGetValue(CurrentBiome, out var _)) { return; } int num = 1; List list = new List(); Player.GetPlayersInRange(base.transform.position, 100f, list); if (list.Count > 0) { num = list.Count; } int num2 = 3 + (num - 1); if (num2 < 3) { num2 = 3; } List value2; bool flag = BlightedItemsManager.BiomeWeaponDrops.TryGetValue(CurrentBiome, out value2) && value2.Count > 0; List value3; bool flag2 = BlightedItemsManager.BiomeArmorDrops.TryGetValue(CurrentBiome, out value3) && value3.Count > 0; if (!flag && !flag2) { return; } for (int i = 0; i < num2; i++) { bool flag3 = Random.value > 0.5f; if ((flag3 && flag) || !flag2) { string prefabName = value2[Random.Range(0, value2.Count)]; SpawnDrop(prefabName); } else if (flag2) { string prefabName2 = value3[Random.Range(0, value3.Count)]; SpawnDrop(prefabName2); } } } private void SpawnDrop(string prefabName) { GameObject gameObject = ZNetScene.instance?.GetPrefab(prefabName); if (gameObject != null) { Vector3 position = base.transform.position + Vector3.up * 1f + Random.insideUnitSphere * 0.5f; Object.Instantiate(gameObject, position, Quaternion.identity); } } private void OnDestroy() { if (BlightedUIManager.Instance != null) { BlightedUIManager.Instance.UnregisterHeart(this); } } public void Initialize(Heightmap.Biome biome) { CurrentBiome = biome; if (BlightedConfigManager.BiomeConfigs.TryGetValue(biome, out var value)) { float num = ((value.WaveTimerSeconds != null) ? value.WaveTimerSeconds.Value : _waveInterval); _lastWaveTime = Time.time - num + 1f; } else { _lastWaveTime = Time.time - _waveInterval + 1f; } if (BlightedConfigManager.BiomeConfigs.TryGetValue(biome, out var value2)) { m_maxHealth = value2.BossHP.Value; m_health = m_maxHealth; if (m_nview != null && m_nview.GetZDO() != null) { m_nview.GetZDO().Set(ZDO_Health, m_health); m_nview.GetZDO().Set(ZDO_MaxHealth, m_maxHealth); } } ApplyBiomeVisuals(biome); } private void ApplyBiomeVisuals(Heightmap.Biome biome) { Renderer[] componentsInChildren = GetComponentsInChildren(); Color biomeColor = GetBiomeColor(biome); Renderer[] array = componentsInChildren; foreach (Renderer renderer in array) { if (renderer.material != null) { renderer.material.SetColor("_Color", biomeColor); if (renderer.material.HasProperty("_EmissionColor")) { renderer.material.SetColor("_EmissionColor", biomeColor * 2f); renderer.material.EnableKeyword("_EMISSION"); } } } } private Color GetBiomeColor(Heightmap.Biome biome) { return biome switch { Heightmap.Biome.Meadows => new Color(0.2f, 0.8f, 0.2f), Heightmap.Biome.BlackForest => new Color(0.5f, 0.1f, 0.8f), Heightmap.Biome.Swamp => new Color(0.4f, 0.3f, 0.1f), Heightmap.Biome.Mountain => new Color(0.8f, 0.9f, 1f), Heightmap.Biome.Plains => new Color(0.9f, 0.7f, 0.2f), Heightmap.Biome.Mistlands => new Color(0.3f, 0.1f, 0.5f), Heightmap.Biome.AshLands => new Color(1f, 0.2f, 0f), Heightmap.Biome.DeepNorth => new Color(0.9f, 0.9f, 1f), _ => Color.white, }; } private void Update() { if (m_nview == null || m_isDead) { return; } if (!m_nview.IsOwner()) { if (m_nview.GetZDO() == null) { return; } m_health = m_nview.GetZDO().GetFloat(ZDO_Health, m_health); m_maxHealth = m_nview.GetZDO().GetFloat(ZDO_MaxHealth, m_maxHealth); if (m_subBoss1 == null) { ZDOID zDOID = m_nview.GetZDO().GetZDOID(ZDO_SubBoss1); if (zDOID != ZDOID.None) { GameObject gameObject = ZNetScene.instance.FindInstance(zDOID); if ((bool)gameObject) { m_subBoss1 = gameObject.GetComponent(); } } } if (!(m_subBoss2 == null)) { return; } ZDOID zDOID2 = m_nview.GetZDO().GetZDOID(ZDO_SubBoss2); if (zDOID2 != ZDOID.None) { GameObject gameObject2 = ZNetScene.instance.FindInstance(zDOID2); if ((bool)gameObject2) { m_subBoss2 = gameObject2.GetComponent(); } } } else { CheckSubBossThresholds(); HandleWaveSpawning(); } } private void HandleWaveSpawning() { if (BlightedConfigManager.BiomeConfigs.TryGetValue(CurrentBiome, out var value)) { float num = ((value.WaveTimerSeconds != null) ? value.WaveTimerSeconds.Value : _waveInterval); if (Time.time - _lastWaveTime > num) { _lastWaveTime = Time.time; SpawnWave(); } } } private void SpawnWave() { if (!BlightedConfigManager.BiomeConfigs.TryGetValue(CurrentBiome, out var value)) { return; } int value2 = value.MaxEnemiesPerWave.Value; m_spawnedEnemies.RemoveAll((GameObject e) => e == null || e.GetComponent() == null || e.GetComponent().IsDead()); int num = value2 - m_spawnedEnemies.Count; if (num <= 0) { return; } string[] waveEnemiesForBiome = GetWaveEnemiesForBiome(CurrentBiome); if (waveEnemiesForBiome.Length == 0) { return; } if (BlightedConfigManager.DebugMode.Value) { Logger.LogInfo((object)string.Format("Attempting to spawn {0} wave enemies (Current: {1}/{2}) of type {3} in {4}.", num, m_spawnedEnemies.Count, value2, string.Join(",", waveEnemiesForBiome), CurrentBiome)); } int num2 = 0; for (int num3 = 0; num3 < num; num3++) { string text = waveEnemiesForBiome[Random.Range(0, waveEnemiesForBiome.Length)]; GameObject prefab = ZNetScene.instance.GetPrefab(text); if (prefab != null) { Vector3 vector = base.transform.position + Random.insideUnitSphere * 10f; if (ZoneSystem.instance.GetSolidHeight(vector, out var height)) { vector.y = height; } else { vector.y = base.transform.position.y; } GameObject gameObject = Object.Instantiate(prefab, vector, Quaternion.identity); m_spawnedEnemies.Add(gameObject); num2++; CharacterDrop[] componentsInChildren = gameObject.GetComponentsInChildren(includeInactive: true); CharacterDrop[] array = componentsInChildren; foreach (CharacterDrop obj in array) { Object.Destroy(obj); } BaseAI component = gameObject.GetComponent(); if (!(component != null)) { continue; } component.Alert(); Player closestPlayer = Player.GetClosestPlayer(vector, 50f); if (closestPlayer != null) { MonsterAI component2 = gameObject.GetComponent(); if (component2 != null) { component2.SetTarget(closestPlayer.GetComponent()); } } } else if (BlightedConfigManager.DebugMode.Value) { Logger.LogWarning((object)("Wave enemy prefab " + text + " not found in ZNetScene.")); } } if (BlightedConfigManager.DebugMode.Value) { Logger.LogInfo((object)$"Successfully spawned {num2} wave enemies."); } if (num2 > 0 && BlightedConfigManager.EnableWaveAnnouncement.Value) { Player.MessageAllInRange(base.transform.position, 100f, BlightedConfigManager.WaveAnnouncementLocation.Value, "The Blighted World Heart cries out for the fallen..."); } } private void CheckSubBossThresholds() { if (BlightedConfigManager.BiomeConfigs.TryGetValue(CurrentBiome, out var value)) { float num = m_health / m_maxHealth; if (BlightedConfigManager.DebugMode.Value && Time.time - _lastLogTime > 5f) { _lastLogTime = Time.time; Logger.LogInfo((object)$"Checking SubBoss thresholds. Current Health: {num:P0}. T1: {value.SubBossThreshold1.Value:P0}, T2: {value.SubBossThreshold2.Value:P0}. Spawned: {_subBoss1Spawned}, {_subBoss2Spawned}"); } if (!_subBoss1Spawned && num <= value.SubBossThreshold1.Value) { _subBoss1Spawned = true; SpawnSubBoss(1, value.SubBoss1HP.Value); } if (!_subBoss2Spawned && num <= value.SubBossThreshold2.Value) { _subBoss2Spawned = true; SpawnSubBoss(2, value.SubBoss2HP.Value); } } } private void SpawnSubBoss(int index, float maxHealth) { string subBossForBiome = GetSubBossForBiome(CurrentBiome); if (BlightedConfigManager.DebugMode.Value) { Logger.LogInfo((object)$"Attempting to spawn subboss: {subBossForBiome} for biome {CurrentBiome}"); } GameObject prefab = ZNetScene.instance.GetPrefab(subBossForBiome); if (!(prefab != null)) { return; } Vector3 vector = base.transform.position + Random.insideUnitSphere * 5f; if (ZoneSystem.instance.GetSolidHeight(vector, out var height)) { vector.y = height; } else { vector.y = base.transform.position.y; } GameObject gameObject = Object.Instantiate(prefab, vector, Quaternion.identity); if (BlightedConfigManager.DebugMode.Value) { Logger.LogInfo((object)("Successfully spawned subboss " + subBossForBiome)); } gameObject.transform.localScale *= 1.5f; CharacterDrop[] componentsInChildren = gameObject.GetComponentsInChildren(includeInactive: true); CharacterDrop[] array = componentsInChildren; foreach (CharacterDrop obj in array) { Object.Destroy(obj); } Component[] componentsInChildren2 = gameObject.GetComponentsInChildren(includeInactive: true); foreach (Component component in componentsInChildren2) { string text = component.GetType().Name; if (text == "DropOnDestroy" || text == "SpawnOnDestroy") { Object.Destroy(component); } } Character component2 = gameObject.GetComponent(); if (component2 != null) { component2.SetLevel(3); component2.SetMaxHealth(maxHealth); component2.SetHealth(maxHealth); } gameObject.AddComponent(); switch (index) { case 1: m_subBoss1 = component2; if (m_nview != null && m_nview.GetZDO() != null && component2 != null) { ZNetView component4 = component2.GetComponent(); if (component4 != null && component4.GetZDO() != null) { m_nview.GetZDO().Set(ZDO_SubBoss1, component4.GetZDO().m_uid); } } break; case 2: m_subBoss2 = component2; if (m_nview != null && m_nview.GetZDO() != null && component2 != null) { ZNetView component3 = component2.GetComponent(); if (component3 != null && component3.GetZDO() != null) { m_nview.GetZDO().Set(ZDO_SubBoss2, component3.GetZDO().m_uid); } } break; } } private string[] GetWaveEnemiesForBiome(Heightmap.Biome biome) { return biome switch { Heightmap.Biome.Meadows => new string[3] { "Boar", "Neck", "Greyling" }, Heightmap.Biome.BlackForest => new string[3] { "Greydwarf", "Greydwarf_Elite", "Skeleton" }, Heightmap.Biome.Swamp => new string[3] { "Draugr", "Blob", "Leech" }, Heightmap.Biome.Mountain => new string[2] { "Wolf", "Hatchling" }, Heightmap.Biome.Plains => new string[2] { "Goblin", "Deathsquito" }, Heightmap.Biome.Mistlands => new string[2] { "Seeker", "SeekerBrood" }, Heightmap.Biome.AshLands => new string[2] { "Charred_Melee", "Charred_Archer" }, _ => new string[1] { "Boar" }, }; } private string GetSubBossForBiome(Heightmap.Biome biome) { string[] array = biome switch { Heightmap.Biome.Meadows => new string[2] { "Boar", "Neck" }, Heightmap.Biome.BlackForest => new string[3] { "Greydwarf_Elite", "Troll", "Skeleton_Poison" }, Heightmap.Biome.Swamp => new string[3] { "Draugr_Elite", "Wraith", "Abomination" }, Heightmap.Biome.Mountain => new string[2] { "Fenring", "StoneGolem" }, Heightmap.Biome.Plains => new string[3] { "GoblinBrute", "GoblinShaman", "Lox" }, Heightmap.Biome.Mistlands => new string[2] { "SeekerBrute", "Seeker" }, Heightmap.Biome.AshLands => new string[2] { "Morgen", "Charred_Melee" }, _ => new string[1] { "Boar" }, }; return array[Random.Range(0, array.Length)]; } } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: BlightedHeart.BlightedConfigManager ---- namespace BlightedHeart { public static class BlightedConfigManager { public class BiomeConfig { public ConfigEntry BossHP; public ConfigEntry SubBossThreshold1; public ConfigEntry SubBossThreshold2; public ConfigEntry SubBoss1HP; public ConfigEntry SubBoss2HP; public ConfigEntry LootScaleFactor; public ConfigEntry MaxEnemiesPerWave; public ConfigEntry WaveTimerSeconds; public ConfigEntry AltarSummonItem; public ConfigEntry AltarSummonAmount; } public static Dictionary BiomeConfigs = new Dictionary(); public static ConfigEntry DebugMode; public static ConfigEntry EnableWaveAnnouncement; public static ConfigEntry WaveAnnouncementLocation; public static void SetupConfig(BlightedHeartPlugin plugin) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0069: 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_0077: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown plugin.Config.SaveOnConfigSet = true; DebugMode = plugin.Config.Bind("General", "Debug Mode", defaultValue: false, new ConfigDescription("Enable verbose debug logging.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); EnableWaveAnnouncement = plugin.Config.Bind("General", "Enable Wave Announcement", defaultValue: true, new ConfigDescription("Toggle for wave spawn text.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); WaveAnnouncementLocation = plugin.Config.Bind("General", "Wave Announcement Location", MessageHud.MessageType.Center, new ConfigDescription("Where the wave text displays.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); SetupBiomeConfig(plugin, Heightmap.Biome.Meadows, 3000f, 0.5f, 0.2f, 750f, 1250f, 1.5f, 6, 8f, "HardAntler", 1); SetupBiomeConfig(plugin, Heightmap.Biome.BlackForest, 6000f, 0.5f, 0.2f, 1500f, 2500f, 2f, 7, 8f, "CryptKey", 1); SetupBiomeConfig(plugin, Heightmap.Biome.Swamp, 9000f, 0.5f, 0.2f, 2250f, 3750f, 2.5f, 8, 8f, "Wishbone", 1); SetupBiomeConfig(plugin, Heightmap.Biome.Mountain, 12000f, 0.5f, 0.2f, 3000f, 5000f, 3f, 9, 8f, "DragonTear", 1); SetupBiomeConfig(plugin, Heightmap.Biome.Plains, 18000f, 0.5f, 0.2f, 4500f, 7500f, 3.5f, 10, 8f, "YagluthDrop", 1); SetupBiomeConfig(plugin, Heightmap.Biome.Mistlands, 24000f, 0.5f, 0.2f, 6000f, 10000f, 4.5f, 11, 8f, "QueenDrop", 1); SetupBiomeConfig(plugin, Heightmap.Biome.AshLands, 30000f, 0.5f, 0.2f, 7500f, 12500f, 5.5f, 12, 8f, "FaderDrop", 1); SetupBiomeConfig(plugin, Heightmap.Biome.DeepNorth, 37500f, 0.5f, 0.2f, 9000f, 15000f, 6f, 13, 8f, "FaderDrop", 1); } private static void SetupBiomeConfig(BlightedHeartPlugin plugin, Heightmap.Biome biome, float defaultHP, float defaultThreshold1, float defaultThreshold2, float defaultSubBoss1HP, float defaultSubBoss2HP, float defaultLootScale, int defaultMaxEnemies, float defaultWaveTimer, string defaultSummonItem, int defaultSummonAmount) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown //IL_00af: 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_00bd: Expected O, but got Unknown //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Expected O, but got Unknown //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Expected O, but got Unknown //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Expected O, but got Unknown //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Expected O, but got Unknown string text = biome.ToString(); BiomeConfig biomeConfig = new BiomeConfig(); biomeConfig.BossHP = plugin.Config.Bind(text, "Boss HP", defaultHP, new ConfigDescription("Base HP of the Blighted World Heart when summoned in the " + text + ".", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); biomeConfig.SubBossThreshold1 = plugin.Config.Bind(text, "Sub-Boss Threshold 1", defaultThreshold1, new ConfigDescription("Health percentage (0.0 to 1.0) to spawn the first sub-boss.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); biomeConfig.SubBossThreshold2 = plugin.Config.Bind(text, "Sub-Boss Threshold 2", defaultThreshold2, new ConfigDescription("Health percentage (0.0 to 1.0) to spawn the second sub-boss.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); biomeConfig.SubBoss1HP = plugin.Config.Bind(text, "Sub-Boss 1 HP", defaultSubBoss1HP, new ConfigDescription("Max HP of the first sub-boss.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); biomeConfig.SubBoss2HP = plugin.Config.Bind(text, "Sub-Boss 2 HP", defaultSubBoss2HP, new ConfigDescription("Max HP of the second sub-boss.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); biomeConfig.LootScaleFactor = plugin.Config.Bind(text, "Loot Scale Factor", defaultLootScale, new ConfigDescription("Multiplier for the loot quality and drop amounts.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); biomeConfig.MaxEnemiesPerWave = plugin.Config.Bind(text, "Max Enemies Per Wave", defaultMaxEnemies, new ConfigDescription("Maximum number of standard enemies that can be alive in a single wave.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); biomeConfig.WaveTimerSeconds = plugin.Config.Bind(text, "Wave Timer Seconds", defaultWaveTimer, new ConfigDescription("Time in seconds between enemy wave spawns.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); biomeConfig.AltarSummonItem = plugin.Config.Bind(text, "Altar Summon Item", defaultSummonItem, new ConfigDescription("The prefab name of the item required to summon the boss in this biome.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); biomeConfig.AltarSummonAmount = plugin.Config.Bind(text, "Altar Summon Amount", defaultSummonAmount, new ConfigDescription("The amount of the summon item required.", null, (object)new ConfigurationManagerAttributes { IsAdminOnly = true })); BiomeConfig value = biomeConfig; BiomeConfigs[biome] = value; } } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: BlightedHeart.BlightedHeartPlugin ---- namespace BlightedHeart { [BepInPlugin("wubarrk.blightedworldheart", "BlightedWorldHeart", "1.0.6")] [BepInDependency("com.jotunn.jotunn", BepInDependency.DependencyFlags.HardDependency)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class BlightedHeartPlugin : BaseUnityPlugin { public const string PluginGUID = "wubarrk.blightedworldheart"; public const string PluginName = "BlightedWorldHeart"; public const string PluginVersion = "1.0.6"; public static bool IsWackyDBInstalled; private readonly Harmony _harmony = new Harmony("wubarrk.blightedworldheart"); private void Awake() { BlightedConfigManager.SetupConfig(this); IsWackyDBInstalled = Chainloader.PluginInfos.ContainsKey("WackyMole.WackysDatabase"); if (IsWackyDBInstalled) { Logger.LogInfo((object)"Wacky's Database detected! Enabling WackyDB compatibility mode for Blighted items."); } PrefabManager.OnVanillaPrefabsAvailable += SetupBlightedHeartPrefab; PrefabManager.OnVanillaPrefabsAvailable += BlightedItemsManager.Setup; _harmony.PatchAll(); Logger.LogInfo((object)"BlightedWorldHeart v1.0.6 loaded."); } private void SetupBlightedHeartPrefab() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown PrefabManager.OnVanillaPrefabsAvailable -= SetupBlightedHeartPrefab; Logger.LogInfo((object)"Setting up Blighted World Heart prefab..."); GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("BlightedWorldHeart", "TheHive"); if (gameObject == null) { Logger.LogError((object)"Could not create cloned prefab BlightedWorldHeart!"); return; } CustomPrefab val = new CustomPrefab(gameObject, true); gameObject.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f); ZNetView component = gameObject.GetComponent(); if (component == null) { component = gameObject.AddComponent(); } BlightedWorldHeartAI blightedWorldHeartAI = gameObject.AddComponent(); PrefabManager.Instance.AddPrefab(val); Logger.LogInfo((object)"BlightedWorldHeart prefab registered. Runtime cleanup will happen on spawn."); GameObject gameObject2 = new GameObject("BlightedUIManager"); Object.DontDestroyOnLoad(gameObject2); gameObject2.AddComponent(); } } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: BlightedHeart.BlightedDamageForwarder ---- namespace BlightedHeart { public class BlightedDamageForwarder : MonoBehaviour, IDestructible { public BlightedWorldHeartAI m_heart; public DestructibleType GetDestructibleType() { return (!(m_heart != null)) ? DestructibleType.Default : m_heart.GetDestructibleType(); } public void Damage(HitData hit) { if (m_heart != null) { m_heart.Damage(hit); } } } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: BlightedHeart.BlightedSubBoss ---- namespace BlightedHeart { public class BlightedSubBoss : MonoBehaviour { public Character m_character; private void Awake() { m_character = GetComponent(); if (m_character != null) { m_character.m_boss = false; } } } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: BlightedHeart.PluginInfo ---- namespace BlightedHeart { public static class PluginInfo { public const string PLUGIN_GUID = "BlightedHeart"; public const string PLUGIN_NAME = "BlightedHeart"; public const string PLUGIN_VERSION = "1.0.6"; } } // ---- BlightedWorldHeart\plugins\BlightedHeart.dll :: System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute ---- namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }