using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Jotunn; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("DvergrAllies")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("A Valheim mod that allows taming and crossbreeding Dvergrs.")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: AssemblyInformationalVersion("1.0.2")] [assembly: AssemblyProduct("DvergrAllies")] [assembly: AssemblyTitle("DvergrAllies")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 DvergrAllies { public class CustomDvergrDef { public string PrefabName; public string BasePrefab; public string DisplayName; public string[] Loadout; public float HealthMultiplier = 1f; public bool StripArmor; public bool AddRage; public bool IsMeleeAI; public bool IsRangedAI; public bool IsClericAI; } public static class AllyPrefabManager { public static List CustomVariants = new List { new CustomDvergrDef { PrefabName = "AllyDvergrMageElemental", BasePrefab = "DvergerMageFire", DisplayName = "Dvergr Elemental Mage", Loadout = new string[0], IsRangedAI = true }, new CustomDvergrDef { PrefabName = "AllyDvergrSpellswordFire", BasePrefab = "DvergerMageFire", DisplayName = "Dvergr Fire Spellsword", Loadout = new string[1] { "AllyDvergr_SwordBronze" }, IsMeleeAI = true }, new CustomDvergrDef { PrefabName = "AllyDvergrSpellswordIce", BasePrefab = "DvergerMageIce", DisplayName = "Dvergr Ice Spellsword", Loadout = new string[1] { "AllyDvergr_SwordBronze" }, IsMeleeAI = true }, new CustomDvergrDef { PrefabName = "AllyDvergrCleric", BasePrefab = "DvergerMageSupport", DisplayName = "Dvergr Cleric", Loadout = new string[3] { "AllyDvergr_MaceBronze", "DvergerStaffHeal_heal", "DvergerStaffSupport_buff" }, IsClericAI = true }, new CustomDvergrDef { PrefabName = "AllyDvergrWarrior", BasePrefab = "Dverger", DisplayName = "Dvergr Warrior", Loadout = new string[2] { "AllyDvergr_SwordBronze", "ShieldBanded" }, HealthMultiplier = 1.5f, IsMeleeAI = true }, new CustomDvergrDef { PrefabName = "AllyDvergrBerserker", BasePrefab = "Dverger", DisplayName = "Dvergr Berserker", Loadout = new string[1] { "AllyDvergr_Battleaxe" }, StripArmor = false, AddRage = true, IsMeleeAI = true } }; public static void Setup() { PrefabManager.OnVanillaPrefabsAvailable -= Setup; Logger.LogInfo((object)"Setting up Ally Dvergr prefabs..."); CreateAllyVariant("Dverger", "AllyDvergrRogue", "Dvergr Rogue"); CreateAllyVariant("DvergerMage", "AllyDvergrMage", "Dvergr Mage"); CreateAllyVariant("DvergerMageFire", "AllyDvergrMageFire", "Dvergr Fire Mage"); CreateAllyVariant("DvergerMageIce", "AllyDvergrMageIce", "Dvergr Ice Mage"); CreateAllyVariant("DvergerMageSupport", "AllyDvergrMageSupport", "Dvergr Support Mage"); foreach (CustomDvergrDef customVariant in CustomVariants) { CreateCustomVariant(customVariant); } GetOrCloneWeaponWithMeleeAnim("AllyDvergr_SwordIron"); GetOrCloneWeaponWithMeleeAnim("AllyDvergr_SwordBlackmetal"); GetOrCloneWeaponWithMeleeAnim("AllyDvergr_MaceIron"); GetOrCloneWeaponWithMeleeAnim("AllyDvergr_MaceBlackmetal"); GetOrCloneWeaponWithMeleeAnim("AllyDvergr_BattleaxeBlackmetal"); Logger.LogInfo((object)"Ally Dvergr prefabs created."); } public static void MakePrefabTamable(GameObject prefab) { if ((Object)(object)prefab == (Object)null) { return; } Tameable component = prefab.GetComponent(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } DvergrTameable dvergrTameable = prefab.GetComponent(); if ((Object)(object)dvergrTameable == (Object)null) { dvergrTameable = prefab.AddComponent(); } ((Tameable)dvergrTameable).m_tamingTime = ConfigManager.TamingTime.Value; ((Tameable)dvergrTameable).m_fedDuration = ConfigManager.FedDuration.Value; ((Tameable)dvergrTameable).m_commandable = true; Procreation component2 = prefab.GetComponent(); if ((Object)(object)component2 != (Object)null) { Object.DestroyImmediate((Object)(object)component2); } if ((Object)(object)prefab.GetComponent() == (Object)null) { prefab.AddComponent(); } if ((Object)(object)prefab.GetComponent() == (Object)null) { prefab.AddComponent(); } MonsterAI component3 = prefab.GetComponent(); if (!((Object)(object)component3 != (Object)null)) { return; } ((BaseAI)component3).m_passiveAggresive = false; component3.m_fleeIfNotAlerted = false; component3.m_fleeIfLowHealth = 0f; component3.m_attackPlayerObjects = false; ((BaseAI)component3).m_avoidFire = false; if (component3.m_consumeItems == null) { component3.m_consumeItems = new List(); } string[] array = ConfigManager.TamingItems.Value.Split(new char[1] { ',' }); foreach (string text in array) { GameObject prefab2 = PrefabManager.Instance.GetPrefab(text.Trim()); if ((Object)(object)prefab2 != (Object)null) { ItemDrop component4 = prefab2.GetComponent(); if ((Object)(object)component4 != (Object)null && !component3.m_consumeItems.Contains(component4)) { component3.m_consumeItems.Add(component4); } } } if (component3.m_consumeSearchRange < 10f) { component3.m_consumeSearchRange = 10f; } if (component3.m_consumeSearchInterval < 10f) { component3.m_consumeSearchInterval = 10f; } component3.m_consumeRange = ConfigManager.ConsumeRange.Value; } public static GameObject GetOrCloneWeaponWithMeleeAnim(string itemName) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown if (itemName.StartsWith("AllyDvergr_")) { string text = itemName.Replace("AllyDvergr_", ""); GameObject prefab = PrefabManager.Instance.GetPrefab(itemName); if ((Object)(object)prefab != (Object)null) { return prefab; } if ((Object)(object)PrefabManager.Instance.GetPrefab(text) == (Object)null) { return null; } GameObject obj = PrefabManager.Instance.CreateClonedPrefab(itemName, text); CustomItem val = new CustomItem(obj, true); ItemDrop itemDrop = val.ItemDrop; if ((Object)(object)itemDrop != (Object)null && itemDrop.m_itemData != null && itemDrop.m_itemData.m_shared != null) { if (itemName.Contains("Sword") || itemName.Contains("Mace") || itemName.Contains("Battleaxe") || itemName.Contains("Shield")) { GameObject prefab2 = PrefabManager.Instance.GetPrefab("Dverger_melee"); if ((Object)(object)prefab2 != (Object)null) { ItemDrop component = prefab2.GetComponent(); if ((Object)(object)component != (Object)null && component.m_itemData.m_shared != null) { itemDrop.m_itemData.m_shared.m_attack = component.m_itemData.m_shared.m_attack.Clone(); } } itemDrop.m_itemData.m_shared.m_aiAttackInterval = 1.2f; itemDrop.m_itemData.m_shared.m_aiAttackRange = 3.5f; itemDrop.m_itemData.m_shared.m_attack.m_attackRange = 3.5f; itemDrop.m_itemData.m_shared.m_attack.m_attackStamina = 0f; itemDrop.m_itemData.m_shared.m_attack.m_attackHealth = 0f; itemDrop.m_itemData.m_shared.m_attack.m_attackHealthPercentage = 0f; itemDrop.m_itemData.m_shared.m_attack.m_attackEitr = 0f; itemDrop.m_itemData.m_shared.m_useDurability = false; } if (itemName.Contains("Battleaxe")) { itemDrop.m_itemData.m_shared.m_damages.m_slash *= 1.5f; } float value = ConfigManager.AllyDamageMultiplier.Value; if (value != 1f) { itemDrop.m_itemData.m_shared.m_damages.m_damage *= value; itemDrop.m_itemData.m_shared.m_damages.m_blunt *= value; itemDrop.m_itemData.m_shared.m_damages.m_slash *= value; itemDrop.m_itemData.m_shared.m_damages.m_pierce *= value; itemDrop.m_itemData.m_shared.m_damages.m_chop *= value; itemDrop.m_itemData.m_shared.m_damages.m_pickaxe *= value; itemDrop.m_itemData.m_shared.m_damages.m_fire *= value; itemDrop.m_itemData.m_shared.m_damages.m_frost *= value; itemDrop.m_itemData.m_shared.m_damages.m_lightning *= value; itemDrop.m_itemData.m_shared.m_damages.m_poison *= value; itemDrop.m_itemData.m_shared.m_damages.m_spirit *= value; } } ItemManager.Instance.AddItem(val); return obj; } return PrefabManager.Instance.GetPrefab(itemName); } private static string GetSuitName(string basePrefabName) { return basePrefabName switch { "Dverger" => "DvergerSuitArbalest", "DvergerMageFire" => "DvergerSuitFire", "DvergerMageIce" => "DvergerSuitIce", "DvergerMageSupport" => "DvergerSuitSupport", _ => null, }; } private static void CreateCustomVariant(CustomDvergrDef def) { CreateAllyVariant(def.BasePrefab, def.PrefabName, def.DisplayName, def.HealthMultiplier, def.IsMeleeAI, def.IsRangedAI, def.IsClericAI); GameObject prefab = PrefabManager.Instance.GetPrefab(def.PrefabName); if ((Object)(object)prefab == (Object)null) { return; } Humanoid component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { return; } component.m_randomSets = (ItemSet[])(object)new ItemSet[0]; component.m_randomWeapon = (GameObject[])(object)new GameObject[0]; component.m_randomShield = (GameObject[])(object)new GameObject[0]; if (def.StripArmor) { component.m_randomArmor = (GameObject[])(object)new GameObject[0]; } List list = new List(); if (!def.StripArmor) { string suitName = GetSuitName(def.BasePrefab); if (suitName != null) { GameObject prefab2 = PrefabManager.Instance.GetPrefab(suitName); if ((Object)(object)prefab2 != (Object)null) { list.Add(prefab2); ConfigManager.LogEquipment("[CustomVariant] " + def.PrefabName + ": Added suit: " + suitName); } } } string[] loadout = def.Loadout; foreach (string text in loadout) { GameObject orCloneWeaponWithMeleeAnim = GetOrCloneWeaponWithMeleeAnim(text); if ((Object)(object)orCloneWeaponWithMeleeAnim != (Object)null) { list.Add(orCloneWeaponWithMeleeAnim); ConfigManager.LogEquipment("[CustomVariant] " + def.PrefabName + ": Added: " + text); continue; } Logger.LogWarning((object)("[CustomVariant] " + def.PrefabName + ": Could not find prefab '" + text + "'!")); } component.m_defaultItems = list.ToArray(); component.m_unarmedWeapon = null; ConfigManager.LogEquipment($"[CustomVariant] {def.PrefabName}: Final m_defaultItems count: {list.Count}"); if (def.AddRage) { prefab.AddComponent(); } DvergrCombatAI component2 = prefab.GetComponent(); if ((Object)(object)component2 != (Object)null && def.PrefabName == "AllyDvergrMageElemental") { component2.IsElementalMage = true; } } private static void CreateAllyVariant(string basePrefabName, string newPrefabName, string displayName, float healthMultiplier = 1f, bool isMelee = false, bool isRanged = false, bool isCleric = false) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_028f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)PrefabManager.Instance.GetPrefab(basePrefabName) == (Object)null) { Logger.LogWarning((object)("Base prefab " + basePrefabName + " not found!")); return; } GameObject val = PrefabManager.Instance.CreateClonedPrefab(newPrefabName, basePrefabName); if ((Object)(object)val == (Object)null) { return; } CustomPrefab val2 = new CustomPrefab(val, true); StripBalrondComponents(val); Tameable component = val.GetComponent(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } DvergrTameable dvergrTameable = val.GetComponent(); if ((Object)(object)dvergrTameable == (Object)null) { dvergrTameable = val.AddComponent(); } ((Tameable)dvergrTameable).m_tamingTime = ConfigManager.TamingTime.Value; ((Tameable)dvergrTameable).m_fedDuration = ConfigManager.FedDuration.Value; ((Tameable)dvergrTameable).m_commandable = true; MonsterAI component2 = val.GetComponent(); if ((Object)(object)component2 != (Object)null) { string[] array = ConfigManager.TamingItems.Value.Split(new char[1] { ',' }); component2.m_consumeItems = new List(); string[] array2 = array; foreach (string text in array2) { GameObject prefab = PrefabManager.Instance.GetPrefab(text.Trim()); if ((Object)(object)prefab != (Object)null) { ItemDrop component3 = prefab.GetComponent(); if ((Object)(object)component3 != (Object)null) { component2.m_consumeItems.Add(component3); } else { Logger.LogWarning((object)("Item " + text + " is missing ItemDrop component!")); } } else { Logger.LogWarning((object)("Could not find taming item prefab: " + text)); } } component2.m_consumeSearchRange = 10f; component2.m_consumeSearchInterval = 10f; component2.m_consumeRange = ConfigManager.ConsumeRange.Value; ((BaseAI)component2).m_aggravatable = false; ((BaseAI)component2).m_passiveAggresive = false; component2.m_fleeIfLowHealth = 0f; component2.m_fleeIfNotAlerted = false; component2.m_circulateWhileCharging = false; component2.m_circulateWhileChargingFlying = false; ((BaseAI)component2).m_avoidFire = false; ((BaseAI)component2).m_avoidWater = false; component2.m_interceptTimeMax = 2f; component2.m_interceptTimeMin = 0f; component2.m_maxChaseDistance = 0f; component2.m_alertRange = 30f; component2.m_attackPlayerObjects = false; } Procreation component4 = val.GetComponent(); if ((Object)(object)component4 != (Object)null) { Object.DestroyImmediate((Object)(object)component4); } if ((Object)(object)val.GetComponent() == (Object)null) { val.AddComponent(); } val.AddComponent(); val.AddComponent(); DvergrCombatAI dvergrCombatAI = val.AddComponent(); dvergrCombatAI.IsMeleeClass = isMelee; dvergrCombatAI.IsRangedClass = isRanged; dvergrCombatAI.IsClericClass = isCleric; Character component5 = val.GetComponent(); if ((Object)(object)component5 != (Object)null) { component5.m_health *= ConfigManager.AllyHealthMultiplier.Value * healthMultiplier; component5.m_faction = (Faction)0; component5.m_runSpeed = Mathf.Max(component5.m_runSpeed, 6.5f); component5.m_walkSpeed = Mathf.Max(component5.m_walkSpeed, 3f); } Humanoid component6 = val.GetComponent(); if ((Object)(object)component6 != (Object)null) { ((Character)component6).m_name = displayName; } PrefabManager.Instance.AddPrefab(val2); } private static void StripBalrondComponents(GameObject prefab) { if (!Plugin.HasBalrondIdleActors) { return; } string[] array = new string[3] { "BalrondActor", "BalrondActorTalker", "BalrondActorNamePicker" }; foreach (string text in array) { Component component = prefab.GetComponent(text); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); Logger.LogInfo((object)("[DvergrAllies] Stripped " + text + " from cloned " + ((Object)prefab).name)); ConfigManager.LogDebug("[Compat] Removed " + text + " from " + ((Object)prefab).name + " to prevent Balrond Idle Actors conflict."); } } } } [HarmonyPatch(typeof(ZNetScene), "Awake")] public static class Patch_ZNetScene_Awake_Dvergrs { [HarmonyPostfix] public static void Postfix(ZNetScene __instance) { if ((Object)(object)__instance == (Object)null || __instance.m_prefabs == null) { return; } if (Plugin.HasBalrondIdleActors) { Debug.LogError((object)"======================================================================="); Debug.LogError((object)"[DvergrAllies] BalrondIdleActors detected! Skipping wild Dvergr injection!"); Debug.LogError((object)"[DvergrAllies] Enabling compatibility mode: Wild Dvergr modifications are disabled."); Debug.LogError((object)"======================================================================="); return; } Logger.LogInfo((object)"Scanning ZNetScene for wild Dvergr prefabs to make tamable..."); int num = 0; foreach (GameObject prefab in __instance.m_prefabs) { if (!((Object)(object)prefab == (Object)null)) { string text = ((Object)prefab).name.ToLower(); if ((text.Contains("dverg") || text.Contains("dverger")) && !text.Contains("ally") && (Object)(object)prefab.GetComponent() != (Object)null && (Object)(object)prefab.GetComponent() != (Object)null) { AllyPrefabManager.MakePrefabTamable(prefab); num++; } } } Logger.LogInfo((object)$"Successfully injected Taming/Breeding components into {num} wild Dvergr prefabs!"); } } [HarmonyPatch(typeof(MonsterAI), "UpdateAI")] public static class Patch_MonsterAI_UpdateAI_Dvergrs { [HarmonyPrefix] public static void Prefix(MonsterAI __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)((Component)__instance).gameObject == (Object)null)) { string text = ((Object)((Component)__instance).gameObject).name.ToLower(); if ((text.Contains("dverg") || text.Contains("dverger")) && !text.Contains("ally") && !Plugin.HasBalrondIdleActors) { ((BaseAI)__instance).m_passiveAggresive = false; __instance.m_attackPlayerObjects = false; ((BaseAI)__instance).m_avoidFire = false; __instance.m_fleeIfNotAlerted = false; } } } } public class CheckMethods { public static void Dump() { MethodInfo methodInfo = AccessTools.Method(typeof(MonsterAI), "Awake", (Type[])null, (Type[])null); Logger.LogInfo((object)("MonsterAI Awake is " + ((methodInfo == null) ? "NULL" : methodInfo.DeclaringType.Name))); methodInfo = AccessTools.Method(typeof(BaseAI), "Awake", (Type[])null, (Type[])null); Logger.LogInfo((object)("BaseAI Awake is " + ((methodInfo == null) ? "NULL" : methodInfo.DeclaringType.Name))); } } public static class ConfigManager { public static ConfigEntry TamingTime; public static ConfigEntry TamingItems; public static ConfigEntry FedDuration; public static ConfigEntry ConsumeRange; public static ConfigEntry PregnancyDuration; public static ConfigEntry BreedingLimit; public static ConfigEntry SpecialComboChance; public static ConfigEntry PregnancyChance; public static ConfigEntry PartnerCheckRange; public static ConfigEntry UpdateInterval; public static ConfigEntry LevelUpChance; public static ConfigEntry MaxBreedingLevel; public static ConfigEntry MaxFollowLeash; public static ConfigEntry ContractCost; public static ConfigEntry AllyHealthMultiplier; public static ConfigEntry AllyDamageMultiplier; public static ConfigEntry EnableDebugLogs; public static ConfigEntry DebugFull; public static ConfigEntry DebugAI; public static ConfigEntry DebugBreeding; public static ConfigEntry DebugEquipment; public static void LogAI(string message) { if (EnableDebugLogs != null && EnableDebugLogs.Value && (DebugFull.Value || DebugAI.Value)) { Logger.LogInfo((object)("[AI] " + message)); } } public static void LogBreeding(string message) { if (EnableDebugLogs != null && EnableDebugLogs.Value && (DebugFull.Value || DebugBreeding.Value)) { Logger.LogInfo((object)("[Breeding] " + message)); } } public static void LogEquipment(string message) { if (EnableDebugLogs != null && EnableDebugLogs.Value && (DebugFull.Value || DebugEquipment.Value)) { Logger.LogInfo((object)("[Equipment] " + message)); } } public static void LogDebug(string message) { if (EnableDebugLogs != null && EnableDebugLogs.Value && DebugFull.Value) { Logger.LogInfo((object)("[General] " + message)); } } private static ConfigDescription SyncedConfig(string description) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } }); } public static void Init(ConfigFile config) { TamingTime = config.Bind("1 - Taming", "Taming Time", 1800f, SyncedConfig("Time in seconds required to tame a Dvergr.")); TamingItems = config.Bind("1 - Taming", "Taming Items", "CookedMeat,Coins,Sausages,YggdrasilWood", SyncedConfig("Comma-separated list of item prefabs Dvergrs will eat to tame/breed.")); FedDuration = config.Bind("1 - Taming", "Fed Duration", 600f, SyncedConfig("Time in seconds a Dvergr stays fed (not hungry) after eating.")); ConsumeRange = config.Bind("1 - Taming", "Consume Range", 4f, SyncedConfig("Distance from which they can consume food. Increase if they get stuck pushing each other.")); PregnancyDuration = config.Bind("2 - Breeding", "Pregnancy Duration", 600f, SyncedConfig("Time in seconds before a pregnant Dvergr gives birth.")); BreedingLimit = config.Bind("2 - Breeding", "Breeding Limit", 4, SyncedConfig("Maximum number of Dvergrs in a 10m radius before they stop breeding.")); SpecialComboChance = config.Bind("2 - Breeding", "Special Combo Chance", 25f, SyncedConfig("Percentage chance (0-100) to breed a special combo variant if parents are compatible.")); PregnancyChance = config.Bind("2 - Breeding", "Pregnancy Chance", 0.33f, SyncedConfig("Chance (0.0 to 1.0) of getting pregnant when conditions are met.")); PartnerCheckRange = config.Bind("2 - Breeding", "Partner Check Range", 10f, SyncedConfig("Distance within which to look for a partner.")); UpdateInterval = config.Bind("2 - Breeding", "Update Interval", 10f, SyncedConfig("How often (in seconds) the breeding logic checks for partners/pregnancy.")); LevelUpChance = config.Bind("2 - Breeding", "Level Up Chance", 10f, SyncedConfig("Percentage chance (0-100) for offspring to gain +1 level (star) above their highest level parent.")); MaxBreedingLevel = config.Bind("2 - Breeding", "Max Breeding Level", 3, SyncedConfig("Maximum level (3 = 2 stars) a Dvergr can reach through breeding.")); MaxFollowLeash = config.Bind("2.5 - AI", "Max Follow Leash", 40f, SyncedConfig("Max distance (meters) a Dvergr can chase an enemy before dropping aggro to return to the player they are following.")); ContractCost = config.Bind("3 - Economy", "Contract Cost", 999, SyncedConfig("Cost in coins to purchase a Dvergr Contract from Haldor or Recruiter.")); AllyHealthMultiplier = config.Bind("4 - Stats", "Health Multiplier", 1f, SyncedConfig("Multiplier applied to base health of Ally Dvergrs.")); AllyDamageMultiplier = config.Bind("4 - Stats", "Damage Multiplier", 1f, SyncedConfig("Multiplier applied to base damage of Ally Dvergrs.")); EnableDebugLogs = config.Bind("5 - Debug", "Enable Debug Logs", false, SyncedConfig("Master switch to enable debug logging.")); DebugFull = config.Bind("5 - Debug", "Debug Full", false, SyncedConfig("If true, overrides sub-categories and logs absolutely everything.")); DebugAI = config.Bind("5 - Debug", "Debug AI", false, SyncedConfig("Log AI behavior overrides, targeting, and behavior resets.")); DebugBreeding = config.Bind("5 - Debug", "Debug Breeding", false, SyncedConfig("Log partner finding, pregnancy, birth, and genetic scaling.")); DebugEquipment = config.Bind("5 - Debug", "Debug Equipment", false, SyncedConfig("Log prefab creation, loadout setup, and weapon scaling/upgrades.")); } } public static class CustomStavesManager { public static void Setup() { PrefabManager.OnVanillaPrefabsAvailable -= Setup; CreateEarthStaff(); CreateSpiritStaff(); } private static void CreateEarthStaff() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown if ((Object)(object)PrefabManager.Instance.GetPrefab("DvergerStaffFire") == (Object)null) { return; } CustomItem val = new CustomItem("AllyDvergrStaffEarth", "DvergerStaffFire"); ItemDrop itemDrop = val.ItemDrop; itemDrop.m_itemData.m_shared.m_name = "Earth Staff"; if ((Object)(object)PrefabManager.Instance.GetPrefab("DvergerStaffIce_projectile") != (Object)null) { GameObject val2 = PrefabManager.Instance.CreateClonedPrefab("AllyDvergrStaffEarth_projectile", "DvergerStaffIce_projectile"); Projectile component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { component.m_damage.m_frost = 0f; component.m_damage.m_blunt = 20f; component.m_damage.m_poison = 40f; component.m_statusEffect = "Tar"; GameObject prefab = PrefabManager.Instance.GetPrefab("vfx_blob_attack"); if ((Object)(object)prefab != (Object)null) { component.m_hitEffects.m_effectPrefabs = (EffectData[])(object)new EffectData[1] { new EffectData { m_prefab = prefab, m_enabled = true } }; } } itemDrop.m_itemData.m_shared.m_attack.m_attackProjectile = val2; } ItemManager.Instance.AddItem(val); } private static void CreateSpiritStaff() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown if ((Object)(object)PrefabManager.Instance.GetPrefab("DvergerStaffFire") == (Object)null) { return; } CustomItem val = new CustomItem("AllyDvergrStaffSpirit", "DvergerStaffFire"); ItemDrop itemDrop = val.ItemDrop; itemDrop.m_itemData.m_shared.m_name = "Spirit Staff"; if ((Object)(object)PrefabManager.Instance.GetPrefab("DvergerStaffIce_projectile") != (Object)null) { GameObject val2 = PrefabManager.Instance.CreateClonedPrefab("AllyDvergrStaffSpirit_projectile", "DvergerStaffIce_projectile"); Projectile component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { component.m_damage.m_frost = 0f; component.m_damage.m_blunt = 20f; component.m_damage.m_spirit = 60f; } itemDrop.m_itemData.m_shared.m_attack.m_attackProjectile = val2; } ItemManager.Instance.AddItem(val); } } public class DummyCheck { public void Check() { } } public class DvergrBerserkerRage : MonoBehaviour { private Character m_character; private void Start() { m_character = ((Component)this).GetComponent(); ((MonoBehaviour)this).InvokeRepeating("ApplyRage", 1f, 5f); } private void ApplyRage() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)m_character == (Object)null) && !m_character.IsDead()) { SEMan sEMan = m_character.GetSEMan(); if (sEMan != null && !sEMan.HaveStatusEffect(StringExtensionMethods.GetStableHashCode("BerserkerRage"))) { SE_Stats val = ScriptableObject.CreateInstance(); ((Object)val).name = "BerserkerRage"; ((StatusEffect)val).m_name = "Rage"; val.m_speedModifier = 1.15f; val.m_staminaRegenMultiplier = 1.3f; val.m_healthRegenMultiplier = 1.2f; val.m_raiseSkill = (SkillType)7; val.m_raiseSkillModifier = 15f; sEMan.AddStatusEffect((StatusEffect)(object)val, false, 0, 0f); } } } } public class DvergrCombatAI : MonoBehaviour { public bool IsMeleeClass; public bool IsRangedClass; public bool IsClericClass; public bool IsElementalMage; private MonsterAI m_monsterAI; private Character m_character; private bool m_initialized; private bool m_isConfigured; private float m_clericHealTimer = 15f; private float m_healScanTimer; private float m_elementRotateTimer; private List m_elementalStaves = new List(); private int m_elementSetIndex; private void Awake() { m_monsterAI = ((Component)this).GetComponent(); m_character = ((Component)this).GetComponent(); m_isConfigured = false; if (!Plugin.HasBalrondIdleActors) { return; } string[] array = new string[3] { "BalrondActor", "BalrondActorTalker", "BalrondActorNamePicker" }; foreach (string text in array) { Component component = ((Component)this).GetComponent(text); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); ConfigManager.LogDebug("[Compat] Runtime stripped " + text + " from existing active " + ((Object)((Component)this).gameObject).name); } } } private void Start() { ForceAggressiveAI(); ((MonoBehaviour)this).Invoke("ForceAggressiveAI", 1f); ((MonoBehaviour)this).Invoke("ForceAggressiveAI", 3f); ((MonoBehaviour)this).Invoke("InjectMagicWeapons", 0.5f); } private void ForceAggressiveAI() { if (!((Object)(object)m_monsterAI == (Object)null)) { ((BaseAI)m_monsterAI).m_aggravatable = false; ((BaseAI)m_monsterAI).m_passiveAggresive = false; m_monsterAI.m_fleeIfNotAlerted = false; ((BaseAI)m_monsterAI).m_avoidFire = false; ((BaseAI)m_monsterAI).m_avoidWater = false; m_monsterAI.m_fleeIfLowHealth = 0f; m_monsterAI.m_attackPlayerObjects = false; if (IsMeleeClass) { m_monsterAI.m_circulateWhileCharging = false; m_monsterAI.m_circulateWhileChargingFlying = false; ((BaseAI)m_monsterAI).m_randomMoveInterval = 0f; ((BaseAI)m_monsterAI).m_randomCircleInterval = 0f; m_monsterAI.m_alertRange = 30f; } else if (IsRangedClass) { m_monsterAI.m_circulateWhileCharging = false; m_monsterAI.m_circulateWhileChargingFlying = false; ((BaseAI)m_monsterAI).m_randomMoveInterval = 10f; } else if (IsClericClass) { m_monsterAI.m_circulateWhileCharging = false; ((BaseAI)m_monsterAI).m_randomMoveInterval = 5f; ((BaseAI)m_monsterAI).m_randomCircleInterval = 5f; m_monsterAI.m_alertRange = 30f; } if (!m_initialized) { m_initialized = true; ConfigManager.LogAI($"[CombatAI] Forced aggressive AI on {((Object)((Component)this).gameObject).name} (Melee:{IsMeleeClass}, Ranged:{IsRangedClass})"); } } } private void FixedUpdate() { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)m_monsterAI == (Object)null) { return; } if (Time.frameCount % 60 == 0 && (((BaseAI)m_monsterAI).m_aggravatable || ((BaseAI)m_monsterAI).m_passiveAggresive || m_monsterAI.m_fleeIfNotAlerted)) { ConfigManager.LogAI("[CombatAI] AI flags were reset on " + ((Object)((Component)this).gameObject).name + ", re-forcing!"); ForceAggressiveAI(); } if (IsMeleeClass || IsClericClass) { ((BaseAI)m_monsterAI).m_randomCircleInterval = 9999f; m_monsterAI.m_circulateWhileCharging = false; } GameObject followTarget = m_monsterAI.GetFollowTarget(); if ((Object)(object)followTarget != (Object)null && (Object)(object)((BaseAI)m_monsterAI).GetTargetCreature() != (Object)null) { float num = Vector3.Distance(((Component)this).transform.position, followTarget.transform.position); if (num > ConfigManager.MaxFollowLeash.Value) { m_monsterAI.SetTarget((Character)null); ConfigManager.LogAI($"[Leash] {((Object)((Component)this).gameObject).name} strayed too far ({num:F1}m > {ConfigManager.MaxFollowLeash.Value}m), dropping aggro to return to leader."); } } if (IsClericClass) { m_clericHealTimer += Time.fixedDeltaTime; m_healScanTimer += Time.fixedDeltaTime; if (m_healScanTimer >= 2f) { m_healScanTimer = 0f; UpdateClericAI(); } } if (IsElementalMage) { UpdateElementalMageAI(); } } private void UpdateClericAI() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) if (((Object)(object)((BaseAI)m_monsterAI).m_character != (Object)null && ((BaseAI)m_monsterAI).m_character.InAttack()) || m_clericHealTimer < 15f) { return; } bool flag = false; int level = ((BaseAI)m_monsterAI).m_character.GetLevel(); Collider[] array = Physics.OverlapSphere(((Component)this).transform.position, 15f, LayerMask.GetMask(new string[3] { "character", "character_net", "character_ghost" })); for (int i = 0; i < array.Length; i++) { Character component = ((Component)array[i]).GetComponent(); if (!((Object)(object)component != (Object)null) || component.IsDead() || (!((Object)(object)component == (Object)(object)((BaseAI)m_monsterAI).m_character) && !component.IsPlayer() && !component.IsTamed()) || !(component.GetHealthPercentage() < 0.8f)) { continue; } if (!flag) { ((BaseAI)m_monsterAI).m_character.GetZAnim().SetTrigger("StaffCast"); flag = true; m_clericHealTimer = 0f; ConfigManager.LogAI($"[Cleric] Triggered AoE HoT Pulse (Level {level})"); if ((Object)(object)ZNetScene.instance != (Object)null) { GameObject prefab = ZNetScene.instance.GetPrefab("vfx_Potion_health_medium"); if ((Object)(object)prefab != (Object)null) { Object.Instantiate(prefab, ((Component)this).transform.position, Quaternion.identity); } GameObject prefab2 = ZNetScene.instance.GetPrefab("vfx_creature_soothed"); if ((Object)(object)prefab2 != (Object)null) { Object.Instantiate(prefab2, ((Component)this).transform.position + Vector3.up, Quaternion.identity); } } } DvergrHoT[] components = ((Component)component).gameObject.GetComponents(); for (int j = 0; j < components.Length; j++) { Object.Destroy((Object)(object)components[j]); } ((Component)component).gameObject.AddComponent().Setup(level); } } private ItemData CreateNativeVariant(string baseWeaponName, string newProjectileName = null) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown if ((Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return null; } GameObject val = ObjectDB.instance.GetItemPrefab(baseWeaponName) ?? ZNetScene.instance.GetPrefab(baseWeaponName); if ((Object)(object)val == (Object)null) { ConfigManager.LogAI("[NativeVariant] Base weapon " + baseWeaponName + " not found!"); return null; } ItemData val2 = val.GetComponent().m_itemData.Clone(); val2.m_dropPrefab = val; SharedData shared = val2.m_shared; val2.m_shared = new SharedData(); FieldInfo[] fields = typeof(SharedData).GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(val2.m_shared, fieldInfo.GetValue(shared)); } val2.m_shared.m_attack = shared.m_attack.Clone(); if (shared.m_secondaryAttack != null) { val2.m_shared.m_secondaryAttack = shared.m_secondaryAttack.Clone(); } val2.m_shared.m_attack.m_attackStamina = 0f; val2.m_shared.m_attack.m_attackHealth = 0f; val2.m_shared.m_attack.m_attackEitr = 0f; float value = ConfigManager.AllyDamageMultiplier.Value; if (value != 1f) { val2.m_shared.m_damages.m_damage *= value; val2.m_shared.m_damages.m_blunt *= value; val2.m_shared.m_damages.m_slash *= value; val2.m_shared.m_damages.m_pierce *= value; val2.m_shared.m_damages.m_chop *= value; val2.m_shared.m_damages.m_pickaxe *= value; val2.m_shared.m_damages.m_fire *= value; val2.m_shared.m_damages.m_frost *= value; val2.m_shared.m_damages.m_lightning *= value; val2.m_shared.m_damages.m_poison *= value; val2.m_shared.m_damages.m_spirit *= value; } if (newProjectileName != null) { GameObject val3 = ObjectDB.instance.GetItemPrefab(newProjectileName) ?? ZNetScene.instance.GetPrefab(newProjectileName); if ((Object)(object)val3 != (Object)null) { val2.m_shared.m_attack.m_attackProjectile = val3; } } return val2; } private void InjectMagicWeapons() { if ((Object)(object)m_monsterAI == (Object)null || (Object)(object)((BaseAI)m_monsterAI).m_character == (Object)null) { return; } Character character = ((BaseAI)m_monsterAI).m_character; Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null); if ((Object)(object)val == (Object)null || val.GetInventory() == null) { return; } ConfigManager.LogAI($"[RuntimeBuilder] {((Object)((Component)this).gameObject).name} - inventory has {val.GetInventory().GetAllItems().Count} items"); foreach (ItemData allItem in val.GetInventory().GetAllItems()) { if (allItem.IsWeapon() && (Object)(object)allItem.m_dropPrefab != (Object)null && ((Object)allItem.m_dropPrefab).name.Contains("Heal")) { allItem.m_shared.m_aiAttackInterval = 30f; ConfigManager.LogAI("[RuntimeBuilder] NERFED heal staff: " + ((Object)allItem.m_dropPrefab).name + " -> interval=30s"); } } if (((Object)((Component)this).gameObject).name.Contains("SpellswordFire")) { ItemData val2 = CreateNativeVariant("DvergerStaffFire_fireball"); if (val2 != null) { val2.m_shared.m_aiAttackInterval = 15f; val.GetInventory().AddItem(val2); ConfigManager.LogAI("[SpellswordFire] Injected Native Fireball into memory inventory!"); } } else if (((Object)((Component)this).gameObject).name.Contains("SpellswordIce")) { ItemData val3 = CreateNativeVariant("DvergerStaffIce_icebolt"); if (val3 != null) { val3.m_shared.m_aiAttackInterval = 15f; val.GetInventory().AddItem(val3); ConfigManager.LogAI("[SpellswordIce] Injected Native Icebolt into memory inventory!"); } } if (!IsElementalMage) { return; } m_elementalStaves.Clear(); m_elementalStaves.Add(CreateNativeVariant("DvergerStaffFire_fireball")); m_elementalStaves.Add(CreateNativeVariant("DvergerStaffIce_icebolt")); m_elementalStaves.Add(CreateNativeVariant("DvergerStaffFire_fireball", "GoblinShaman_projectile_fireball")); m_elementalStaves.RemoveAll((ItemData x) => x == null); List list = new List(); foreach (ItemData allItem2 in val.GetInventory().GetAllItems()) { if (allItem2.IsWeapon()) { val.UnequipItem(allItem2, true); list.Add(allItem2); } } foreach (ItemData item in list) { val.GetInventory().RemoveItem(item); } if (m_elementalStaves.Count > 0) { val.GetInventory().AddItem(m_elementalStaves[0]); val.EquipItem(m_elementalStaves[0], true); } ConfigManager.LogAI($"[ElementalMage] Loaded {m_elementalStaves.Count} elements into memory cache."); m_elementRotateTimer = 0f; } private void UpdateElementalMageAI() { if ((Object)(object)((BaseAI)m_monsterAI).m_character == (Object)null) { return; } Character character = ((BaseAI)m_monsterAI).m_character; Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null); if ((Object)(object)val == (Object)null) { return; } Inventory inventory = val.GetInventory(); if (inventory == null || m_elementalStaves.Count == 0) { return; } m_elementRotateTimer += Time.fixedDeltaTime; if (!(m_elementRotateTimer >= 10f)) { return; } m_elementRotateTimer = 0f; m_elementSetIndex = (m_elementSetIndex + 1) % m_elementalStaves.Count; ItemData val2 = m_elementalStaves[m_elementSetIndex]; List list = new List(); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem.IsWeapon()) { val.UnequipItem(allItem, true); list.Add(allItem); } } foreach (ItemData item in list) { inventory.RemoveItem(item); } inventory.AddItem(val2); val.EquipItem(val2, true); ConfigManager.LogAI($"[ElementalMage] Swapped to element {m_elementSetIndex}"); } } public class DvergrGenetics : MonoBehaviour { public enum Gender { Male, Female } private ZNetView m_nview; private Humanoid m_humanoid; private void Awake() { m_nview = ((Component)this).GetComponent(); m_humanoid = ((Component)this).GetComponent(); if (!((Object)(object)m_nview == (Object)null) && m_nview.IsValid() && m_nview.IsOwner() && m_nview.GetZDO().GetInt("dvergr_gender", -1) == -1) { Gender gender = ((!(Random.value > 0.5f)) ? Gender.Female : Gender.Male); m_nview.GetZDO().Set("dvergr_gender", (int)gender); ConfigManager.LogBreeding($"[Genetics] {((Object)((Component)this).gameObject).name} randomly assigned gender: {gender}"); } } private void LateUpdate() { if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid() || !((Object)(object)Player.m_localPlayer != (Object)null) || !((Object)(object)((Humanoid)Player.m_localPlayer).GetHoverObject() != (Object)null)) { return; } GameObject hoverObject = ((Humanoid)Player.m_localPlayer).GetHoverObject(); if ((!((Object)(object)hoverObject == (Object)(object)((Component)this).gameObject) && !hoverObject.transform.IsChildOf(((Component)this).transform)) || !((Object)(object)Hud.instance != (Object)null) || !((Object)(object)Hud.instance.m_hoverName != (Object)null)) { return; } int num = m_nview.GetZDO().GetInt("dvergr_gender", -1); if (num != -1) { Gender gender = (Gender)num; string text = $"\n({gender})"; DvergrProcreation component = ((Component)this).GetComponent(); if ((Object)(object)component != (Object)null && component.IsPregnant()) { int pregnancyTimeLeft = component.GetPregnancyTimeLeft(); string text2 = ((pregnancyTimeLeft > 0) ? $"{pregnancyTimeLeft}s" : "Imminent"); text = text + "\nPregnant: " + text2 + ""; } DvergrTameable component2 = ((Component)this).GetComponent(); if ((Object)(object)component2 != (Object)null && ((Tameable)component2).IsHungry()) { text += "\nHungry"; } TextMeshProUGUI hoverName = Hud.instance.m_hoverName; ((TMP_Text)hoverName).text = ((TMP_Text)hoverName).text + text; } } public Gender GetGender() { if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid()) { return Gender.Male; } return (Gender)m_nview.GetZDO().GetInt("dvergr_gender", 0); } } public class DvergrHoT : MonoBehaviour { private Character m_target; private int m_ticksRemaining = 5; private float m_healAmountPerTick = 10f; private float m_tickTimer; public void Setup(int clericLevel) { m_target = ((Component)this).GetComponent(); m_healAmountPerTick = 5f + 5f * (float)clericLevel; m_ticksRemaining = 5; m_tickTimer = 1f; object[] array = new object[4]; Character target = m_target; array[0] = ((target != null) ? ((Object)target).name : null); array[1] = clericLevel; array[2] = m_healAmountPerTick; array[3] = m_ticksRemaining; ConfigManager.LogAI(string.Format("[Cleric] Applied HoT to {0} (Lvl {1}): {2} HP/s for {3}s", array)); } private void FixedUpdate() { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)m_target == (Object)null || m_target.IsDead()) { Object.Destroy((Object)(object)this); return; } m_tickTimer += Time.fixedDeltaTime; if (!(m_tickTimer >= 1f)) { return; } m_tickTimer = 0f; m_ticksRemaining--; m_target.Heal(m_healAmountPerTick, true); if ((Object)(object)ZNetScene.instance != (Object)null) { GameObject prefab = ZNetScene.instance.GetPrefab("vfx_Potion_health_medium"); if ((Object)(object)prefab != (Object)null) { Object.Instantiate(prefab, ((Component)m_target).transform.position, Quaternion.identity); } } if (m_ticksRemaining <= 0) { Object.Destroy((Object)(object)this); } } } public class DvergrProcreation : MonoBehaviour { private ZNetView m_nview; private Character m_character; private BaseAI m_baseAI; private DvergrGenetics m_genetics; private Coroutine m_birthingCoroutine; public EffectList m_birthEffects = new EffectList(); public float m_spawnOffset = 1f; private void Awake() { m_nview = ((Component)this).GetComponent(); m_character = ((Component)this).GetComponent(); m_baseAI = ((Component)this).GetComponent(); m_genetics = ((Component)this).GetComponent(); if (!((Object)(object)m_nview == (Object)null) && m_nview.IsValid()) { ((MonoBehaviour)this).InvokeRepeating("Procreate", Random.Range(0f, ConfigManager.UpdateInterval.Value), ConfigManager.UpdateInterval.Value); } } public bool IsPregnant() { if ((Object)(object)m_nview != (Object)null && m_nview.IsValid()) { return m_nview.GetZDO().GetLong("pregnant", 0L) != 0; } return false; } public int GetPregnancyTimeLeft() { if (!IsPregnant()) { return 0; } long num = m_nview.GetZDO().GetLong("pregnant", 0L); if (num == 0L) { return 0; } DateTime dateTime = new DateTime(num); double totalSeconds = (ZNet.instance.GetTime() - dateTime).TotalSeconds; int num2 = Mathf.CeilToInt((float)((double)ConfigManager.PregnancyDuration.Value - totalSeconds)); return Mathf.Max(0, num2); } private void MakePregnant(string partnerPrefab, int partnerLevel) { if (!((Object)(object)m_nview == (Object)null) && m_nview.IsValid()) { m_nview.GetZDO().Set("pregnant", ZNet.instance.GetTime().Ticks); m_nview.GetZDO().Set("dvergr_partner_prefab", partnerPrefab); m_nview.GetZDO().Set("dvergr_partner_level", partnerLevel); } } private void ResetPregnancy() { if (!((Object)(object)m_nview == (Object)null) && m_nview.IsValid()) { m_nview.GetZDO().Set("pregnant", 0L); m_nview.GetZDO().Set("dvergr_partner_prefab", ""); } } private bool ReadyForProcreation() { if (IsPregnant()) { return false; } if ((Object)(object)m_character == (Object)null || !m_character.IsTamed()) { return false; } if ((Object)(object)m_baseAI != (Object)null && m_baseAI.IsAlerted()) { return false; } DvergrTameable component = ((Component)this).GetComponent(); if ((Object)(object)component != (Object)null && ((Tameable)component).IsHungry()) { return false; } return true; } private void Procreate() { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) if (!m_nview.IsValid() || !m_nview.IsOwner()) { return; } if (IsPregnant()) { long num = m_nview.GetZDO().GetLong("pregnant", 0L); if (num != 0L) { DateTime dateTime = new DateTime(num); if ((ZNet.instance.GetTime() - dateTime).TotalSeconds > (double)ConfigManager.PregnancyDuration.Value && m_birthingCoroutine == null) { m_birthingCoroutine = ((MonoBehaviour)this).StartCoroutine(BirthingProcess()); } } } else { if (!ReadyForProcreation()) { return; } string text = ((Object)((Component)this).gameObject).name.Replace("(Clone)", "").Trim(); int num2 = 0; Collider[] array = Physics.OverlapSphere(((Component)this).transform.position, 10f, LayerMask.GetMask(new string[3] { "character", "character_net", "character_ghost" })); for (int i = 0; i < array.Length; i++) { Character component = ((Component)array[i]).GetComponent(); if ((Object)(object)component != (Object)null && component.IsTamed() && (Object)(object)((Component)component).GetComponent() != (Object)null) { num2++; } } if (num2 >= ConfigManager.BreedingLimit.Value) { ConfigManager.LogBreeding($"[Procreation] {text} halted breeding: Population limit reached ({num2}/{ConfigManager.BreedingLimit.Value}) tamed Dvergrs within 10m."); return; } array = Physics.OverlapSphere(((Component)this).transform.position, ConfigManager.PartnerCheckRange.Value, LayerMask.GetMask(new string[3] { "character", "character_net", "character_ghost" })); for (int i = 0; i < array.Length; i++) { Character component2 = ((Component)array[i]).GetComponent(); if (!((Object)(object)component2 != (Object)null) || !((Object)(object)((Component)component2).gameObject != (Object)(object)((Component)this).gameObject)) { continue; } DvergrGenetics component3 = ((Component)component2).GetComponent(); if (!((Object)(object)component3 != (Object)null) || !((Object)(object)m_genetics != (Object)null) || m_genetics.GetGender() == component3.GetGender()) { continue; } DvergrProcreation component4 = ((Component)component2).GetComponent(); if (!((Object)(object)component4 != (Object)null) || !component4.ReadyForProcreation()) { continue; } ConfigManager.LogBreeding($"[Procreation] {text} ({m_genetics.GetGender()}) found compatible partner {((Object)((Component)component2).gameObject).name} ({component3.GetGender()})"); if (Random.value <= ConfigManager.PregnancyChance.Value) { string text2 = ((Object)((Component)component2).gameObject).name.Replace("(Clone)", "").Trim(); int level = component2.GetLevel(); if (m_genetics.GetGender() == DvergrGenetics.Gender.Female) { MakePregnant(text2, level); component4.ResetPregnancy(); ConfigManager.LogBreeding("[Procreation] " + text + " is now PREGNANT!"); } else { string partnerPrefab = ((Object)((Component)this).gameObject).name.Replace("(Clone)", "").Trim(); int level2 = m_character.GetLevel(); component4.MakePregnant(partnerPrefab, level2); ResetPregnancy(); ConfigManager.LogBreeding("[Procreation] Partner " + text2 + " is now PREGNANT!"); } } else { ConfigManager.LogBreeding("[Procreation] " + text + " failed pregnancy chance roll."); } break; } } } private IEnumerator BirthingProcess() { string text = m_nview.GetZDO().GetString("dvergr_partner_prefab", ""); int num = m_nview.GetZDO().GetInt("dvergr_partner_level", 1); ResetPregnancy(); string text2 = ((Object)((Component)this).gameObject).name.Replace("(Clone)", "").Trim(); string text3 = DetermineOffspring(text2, text); ConfigManager.LogBreeding($"[Procreation] BIRTHING PROCESS START: ParentA={text2}(Lvl {m_character.GetLevel()}), ParentB={text}(Lvl {num}) -> Outcome={text3}"); GameObject prefab = PrefabManager.Instance.GetPrefab(text3); if ((Object)(object)prefab != (Object)null) { Vector3 val = ((Component)this).transform.position + ((Component)this).transform.forward * m_spawnOffset; Character component = Object.Instantiate(prefab, val, Quaternion.LookRotation(((Component)this).transform.forward)).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)m_character != (Object)null) { component.SetTamed(m_character.IsTamed()); int num2 = Mathf.Max(m_character.GetLevel(), num); if ((float)Random.Range(0, 100) < ConfigManager.LevelUpChance.Value) { num2++; ConfigManager.LogBreeding("[Procreation] " + text3 + " offspring triggered LEVEL UP CHANCE! Level increased."); } num2 = Mathf.Clamp(num2, 1, ConfigManager.MaxBreedingLevel.Value); component.SetLevel(num2); ConfigManager.LogBreeding($"[Procreation] Birthed {text3} at Level {num2} (Max {ConfigManager.MaxBreedingLevel.Value})"); } if (m_birthEffects != null && m_birthEffects.HasEffects()) { m_birthEffects.Create(((Component)this).transform.position, Quaternion.identity, (Transform)null, 1f, -1); } } m_birthingCoroutine = null; yield break; } private string DetermineOffspring(string parentA, string parentB) { bool flag = parentA.Contains("Fire") || parentB.Contains("Fire"); bool flag2 = parentA.Contains("Ice") || parentB.Contains("Ice"); bool flag3 = parentA.Contains("Support") || parentB.Contains("Support"); bool flag4 = parentA.Contains("Rogue") || parentB.Contains("Rogue"); bool flag5 = parentA.Contains("Warrior") || parentB.Contains("Warrior"); bool flag6 = parentA.Contains("Cleric") || parentB.Contains("Cleric"); if ((float)Random.Range(0, 100) < ConfigManager.SpecialComboChance.Value) { if (flag4 && !flag && !flag2 && !flag3 && !flag5 && !flag6) { return "AllyDvergrWarrior"; } if (flag3 && !flag4 && !flag && !flag2 && !flag5 && !flag6) { return "AllyDvergrCleric"; } if (flag && flag2) { return "AllyDvergrMageElemental"; } if (flag4 && flag && !flag2) { return "AllyDvergrSpellswordFire"; } if (flag4 && flag2 && !flag) { return "AllyDvergrSpellswordIce"; } if (flag5 && flag5) { return "AllyDvergrBerserker"; } } if (!(Random.value > 0.5f)) { return parentB; } return parentA; } } public class DvergrTameable : Tameable, Hoverable { public string GetHoverText() { string text = ((Tameable)this).GetHoverText(); DvergrProcreation component = ((Component)this).GetComponent(); if ((Object)(object)component != (Object)null && component.IsPregnant()) { int pregnancyTimeLeft = component.GetPregnancyTimeLeft(); string text2 = ((pregnancyTimeLeft > 0) ? $"{pregnancyTimeLeft}s" : "Imminent"); text = text + "\nPregnant: " + text2 + ""; } if (((Tameable)this).IsHungry()) { text += "\nHungry"; } return text; } public string GetHoverName() { return ((Tameable)this).GetHoverName(); } } public class DvergrWeaponScaler : MonoBehaviour { private Humanoid m_humanoid; private Character m_character; private ZNetView m_nview; private bool m_scaled; private void Start() { m_humanoid = ((Component)this).GetComponent(); m_character = ((Component)this).GetComponent(); m_nview = ((Component)this).GetComponent(); if ((Object)(object)m_nview != (Object)null && m_nview.IsValid() && m_nview.IsOwner()) { ((MonoBehaviour)this).Invoke("DoScaleWeapons", 1f); } } private void DoScaleWeapons() { if (m_scaled || (Object)(object)m_humanoid == (Object)null || (Object)(object)m_character == (Object)null) { return; } m_scaled = true; int level = m_character.GetLevel(); if (level <= 1) { return; } string prefabName = Utils.GetPrefabName(((Component)this).gameObject); ConfigManager.LogEquipment($"[WeaponScaler] Scaling {prefabName} (level {level})"); Dictionary dictionary = new Dictionary(); if (level == 2) { dictionary["AllyDvergr_SwordBronze"] = "AllyDvergr_SwordIron"; dictionary["AllyDvergr_MaceBronze"] = "AllyDvergr_MaceIron"; dictionary["ShieldBanded"] = "ShieldSilver"; dictionary["AllyDvergr_Battleaxe"] = "AllyDvergr_BattleaxeBlackmetal"; } else { dictionary["AllyDvergr_SwordBronze"] = "AllyDvergr_SwordBlackmetal"; dictionary["AllyDvergr_MaceBronze"] = "AllyDvergr_MaceBlackmetal"; dictionary["ShieldBanded"] = "ShieldBlackmetal"; dictionary["AllyDvergr_Battleaxe"] = "AllyDvergr_BattleaxeBlackmetal"; } ItemData rightItem = m_humanoid.GetRightItem(); if (rightItem != null) { m_humanoid.UnequipItem(rightItem, true); } ItemData leftItem = m_humanoid.GetLeftItem(); if (leftItem != null) { m_humanoid.UnequipItem(leftItem, true); } List allItems = m_humanoid.GetInventory().GetAllItems(); List list = new List(); List list2 = new List(); for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; string text = (((Object)(object)val.m_dropPrefab != (Object)null) ? ((Object)val.m_dropPrefab).name : ""); if (!string.IsNullOrEmpty(text) && dictionary.ContainsKey(text)) { list.Add(i); list2.Add(dictionary[text]); ConfigManager.LogEquipment("[WeaponScaler] Upgrading " + text + " -> " + dictionary[text]); } } for (int num = list.Count - 1; num >= 0; num--) { m_humanoid.GetInventory().RemoveItem(list[num]); } List list3 = new List(); foreach (string item in list2) { GameObject orCloneWeaponWithMeleeAnim = AllyPrefabManager.GetOrCloneWeaponWithMeleeAnim(item); if ((Object)(object)orCloneWeaponWithMeleeAnim != (Object)null) { list3.Add(orCloneWeaponWithMeleeAnim); bool flag = m_humanoid.GetInventory().AddItem(orCloneWeaponWithMeleeAnim, 1); ConfigManager.LogEquipment($"[WeaponScaler] Added {item} to inventory: {flag}"); if (!flag) { continue; } foreach (ItemData allItem in m_humanoid.GetInventory().GetAllItems()) { if ((Object)(object)allItem.m_dropPrefab != (Object)null && ((Object)allItem.m_dropPrefab).name == item) { m_humanoid.EquipItem(allItem, true); ConfigManager.LogEquipment("[WeaponScaler] Equipped " + item + " successfully!"); break; } } } else { Logger.LogWarning((object)("[WeaponScaler] Could not find replacement prefab: " + item)); } } if (list3.Count <= 0) { return; } List list4 = new List(m_humanoid.m_defaultItems); foreach (GameObject item2 in list3) { if (!list4.Contains(item2)) { list4.Add(item2); } } m_humanoid.m_defaultItems = list4.ToArray(); } } [BepInPlugin("wubarrk.dvergrallies", "DvergrAllies", "1.0.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal class Plugin : BaseUnityPlugin { public const string PluginGUID = "wubarrk.dvergrallies"; public const string PluginName = "DvergrAllies"; public const string PluginVersion = "1.0.2"; public static bool HasBalrondIdleActors; private readonly Harmony harmony = new Harmony("wubarrk.dvergrallies"); private void Awake() { HasBalrondIdleActors = Chainloader.PluginInfos.ContainsKey("balrond.astafaraios.BalrondIdleActors"); ConfigManager.Init(((BaseUnityPlugin)this).Config); PrefabManager.OnVanillaPrefabsAvailable += CustomStavesManager.Setup; PrefabManager.OnVanillaPrefabsAvailable += AllyPrefabManager.Setup; PrefabManager.OnVanillaPrefabsAvailable += RecruiterManager.Setup; harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"DvergrAllies v1.0.2 has loaded!"); } private void OnDestroy() { harmony.UnpatchSelf(); } } public static class RecruiterManager { public static void Setup() { PrefabManager.OnVanillaPrefabsAvailable -= Setup; CreateContractItem(); } private static void CreateContractItem() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown if (!((Object)(object)PrefabManager.Instance.GetPrefab("Amber") == (Object)null)) { CustomItem val = new CustomItem("DvergrContract", "Amber"); ItemDrop itemDrop = val.ItemDrop; itemDrop.m_itemData.m_shared.m_name = "Dvergr Contract"; itemDrop.m_itemData.m_shared.m_description = "A binding contract. Consume to summon a loyal Dvergr Rogue."; itemDrop.m_itemData.m_shared.m_itemType = (ItemType)2; SummonDvergrStatusEffect summonDvergrStatusEffect = ScriptableObject.CreateInstance(); ((Object)summonDvergrStatusEffect).name = "SE_SummonDvergr"; ((StatusEffect)summonDvergrStatusEffect).m_name = "Summoning"; ((StatusEffect)summonDvergrStatusEffect).m_ttl = 0.1f; ItemManager.Instance.AddStatusEffect(new CustomStatusEffect((StatusEffect)(object)summonDvergrStatusEffect, false)); itemDrop.m_itemData.m_shared.m_consumeStatusEffect = (StatusEffect)(object)summonDvergrStatusEffect; itemDrop.m_itemData.m_shared.m_food = 0f; itemDrop.m_itemData.m_shared.m_foodStamina = 0f; itemDrop.m_itemData.m_shared.m_foodRegen = 0f; itemDrop.m_itemData.m_shared.m_value = 0; itemDrop.m_itemData.m_dropPrefab = val.ItemPrefab; ItemManager.Instance.AddItem(val); } } } public class SummonDvergrStatusEffect : StatusEffect { private readonly string[] baseTypes = new string[5] { "AllyDvergrRogue", "AllyDvergrMage", "AllyDvergrMageFire", "AllyDvergrMageIce", "AllyDvergrMageSupport" }; public override void Setup(Character character) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) ((StatusEffect)this).Setup(character); if (!((Object)(object)character != (Object)null) || !character.IsPlayer()) { return; } List list = new List(baseTypes); if (AllyPrefabManager.CustomVariants != null) { foreach (CustomDvergrDef customVariant in AllyPrefabManager.CustomVariants) { list.Add(customVariant.PrefabName); } } string text = list[Random.Range(0, list.Count)]; GameObject prefab = PrefabManager.Instance.GetPrefab(text); if ((Object)(object)prefab != (Object)null) { Vector3 val = ((Component)character).transform.position + ((Component)character).transform.forward * 2f; Tameable component = Object.Instantiate(prefab, val, ((Component)character).transform.rotation).GetComponent(); if ((Object)(object)component != (Object)null) { component.Tame(); } string text2 = text.Replace("AllyDvergr", ""); if (text2 == "Rogue") { text2 = "Rogue"; } character.Message((MessageType)2, "Summoned an Ally Dvergr " + text2 + "!", 0, (Sprite)null); } } } [HarmonyPatch(typeof(Trader))] public static class Patches_Trader { [HarmonyPatch("Start")] [HarmonyPostfix] public static void TraderStartPostfix(Trader __instance) { //IL_0094: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown if (!((Object)((Component)__instance).gameObject).name.Contains("Haldor")) { return; } GameObject prefab = PrefabManager.Instance.GetPrefab("DvergrContract"); if (!((Object)(object)prefab != (Object)null)) { return; } foreach (TradeItem item in __instance.m_items) { if ((Object)(object)item.m_prefab != (Object)null && ((Object)((Component)item.m_prefab).gameObject).name == "DvergrContract") { return; } } TradeItem val = new TradeItem { m_prefab = prefab.GetComponent(), m_price = ConfigManager.ContractCost.Value, m_stack = 1, m_requiredGlobalKey = "" }; FieldInfo[] fields = typeof(TradeItem).GetFields(); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(string) && fieldInfo.GetValue(val) == null) { fieldInfo.SetValue(val, ""); } } __instance.m_items.Add(val); } } public static class PluginInfo { public const string PLUGIN_GUID = "DvergrAllies"; public const string PLUGIN_NAME = "DvergrAllies"; public const string PLUGIN_VERSION = "1.0.2"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }