Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of DvergrAllies v1.0.0
DvergrAllies/plugins/DvergrAllies.dll
Decompiled 13 hours agousing 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.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.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("DvergrAllies")] [assembly: AssemblyTitle("DvergrAllies")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.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<CustomDvergrDef> CustomVariants = new List<CustomDvergrDef> { 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<Tameable>(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } DvergrTameable dvergrTameable = prefab.GetComponent<DvergrTameable>(); if ((Object)(object)dvergrTameable == (Object)null) { dvergrTameable = prefab.AddComponent<DvergrTameable>(); } ((Tameable)dvergrTameable).m_tamingTime = ConfigManager.TamingTime.Value; ((Tameable)dvergrTameable).m_fedDuration = ConfigManager.FedDuration.Value; ((Tameable)dvergrTameable).m_commandable = true; Procreation component2 = prefab.GetComponent<Procreation>(); if ((Object)(object)component2 != (Object)null) { Object.DestroyImmediate((Object)(object)component2); } if ((Object)(object)prefab.GetComponent<DvergrProcreation>() == (Object)null) { prefab.AddComponent<DvergrProcreation>(); } if ((Object)(object)prefab.GetComponent<DvergrGenetics>() == (Object)null) { prefab.AddComponent<DvergrGenetics>(); } MonsterAI component3 = prefab.GetComponent<MonsterAI>(); 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<ItemDrop>(); } 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<ItemDrop>(); 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<ItemDrop>(); 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<Humanoid>(); 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<GameObject> list = new List<GameObject>(); 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<DvergrBerserkerRage>(); } DvergrCombatAI component2 = prefab.GetComponent<DvergrCombatAI>(); 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_0289: 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); Tameable component = val.GetComponent<Tameable>(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } DvergrTameable dvergrTameable = val.GetComponent<DvergrTameable>(); if ((Object)(object)dvergrTameable == (Object)null) { dvergrTameable = val.AddComponent<DvergrTameable>(); } ((Tameable)dvergrTameable).m_tamingTime = ConfigManager.TamingTime.Value; ((Tameable)dvergrTameable).m_fedDuration = ConfigManager.FedDuration.Value; ((Tameable)dvergrTameable).m_commandable = true; MonsterAI component2 = val.GetComponent<MonsterAI>(); if ((Object)(object)component2 != (Object)null) { string[] array = ConfigManager.TamingItems.Value.Split(new char[1] { ',' }); component2.m_consumeItems = new List<ItemDrop>(); 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<ItemDrop>(); 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<Procreation>(); if ((Object)(object)component4 != (Object)null) { Object.DestroyImmediate((Object)(object)component4); } if ((Object)(object)val.GetComponent<DvergrProcreation>() == (Object)null) { val.AddComponent<DvergrProcreation>(); } val.AddComponent<DvergrGenetics>(); val.AddComponent<DvergrWeaponScaler>(); DvergrCombatAI dvergrCombatAI = val.AddComponent<DvergrCombatAI>(); dvergrCombatAI.IsMeleeClass = isMelee; dvergrCombatAI.IsRangedClass = isRanged; dvergrCombatAI.IsClericClass = isCleric; Character component5 = val.GetComponent<Character>(); 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<Humanoid>(); if ((Object)(object)component6 != (Object)null) { ((Character)component6).m_name = displayName; } PrefabManager.Instance.AddPrefab(val2); } } [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; } 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<Humanoid>() != (Object)null && (Object)(object)prefab.GetComponent<MonsterAI>() != (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")) { ((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<float> TamingTime; public static ConfigEntry<string> TamingItems; public static ConfigEntry<float> FedDuration; public static ConfigEntry<float> ConsumeRange; public static ConfigEntry<float> PregnancyDuration; public static ConfigEntry<int> BreedingLimit; public static ConfigEntry<float> SpecialComboChance; public static ConfigEntry<float> PregnancyChance; public static ConfigEntry<float> PartnerCheckRange; public static ConfigEntry<float> UpdateInterval; public static ConfigEntry<float> LevelUpChance; public static ConfigEntry<int> MaxBreedingLevel; public static ConfigEntry<float> MaxFollowLeash; public static ConfigEntry<int> ContractCost; public static ConfigEntry<float> AllyHealthMultiplier; public static ConfigEntry<float> AllyDamageMultiplier; public static ConfigEntry<bool> EnableDebugLogs; public static ConfigEntry<bool> DebugFull; public static ConfigEntry<bool> DebugAI; public static ConfigEntry<bool> DebugBreeding; public static ConfigEntry<bool> 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<float>("1 - Taming", "Taming Time", 1800f, SyncedConfig("Time in seconds required to tame a Dvergr.")); TamingItems = config.Bind<string>("1 - Taming", "Taming Items", "CookedMeat,Coins,Sausages,YggdrasilWood", SyncedConfig("Comma-separated list of item prefabs Dvergrs will eat to tame/breed.")); FedDuration = config.Bind<float>("1 - Taming", "Fed Duration", 600f, SyncedConfig("Time in seconds a Dvergr stays fed (not hungry) after eating.")); ConsumeRange = config.Bind<float>("1 - Taming", "Consume Range", 4f, SyncedConfig("Distance from which they can consume food. Increase if they get stuck pushing each other.")); PregnancyDuration = config.Bind<float>("2 - Breeding", "Pregnancy Duration", 600f, SyncedConfig("Time in seconds before a pregnant Dvergr gives birth.")); BreedingLimit = config.Bind<int>("2 - Breeding", "Breeding Limit", 4, SyncedConfig("Maximum number of Dvergrs in a 10m radius before they stop breeding.")); SpecialComboChance = config.Bind<float>("2 - Breeding", "Special Combo Chance", 25f, SyncedConfig("Percentage chance (0-100) to breed a special combo variant if parents are compatible.")); PregnancyChance = config.Bind<float>("2 - Breeding", "Pregnancy Chance", 0.33f, SyncedConfig("Chance (0.0 to 1.0) of getting pregnant when conditions are met.")); PartnerCheckRange = config.Bind<float>("2 - Breeding", "Partner Check Range", 10f, SyncedConfig("Distance within which to look for a partner.")); UpdateInterval = config.Bind<float>("2 - Breeding", "Update Interval", 10f, SyncedConfig("How often (in seconds) the breeding logic checks for partners/pregnancy.")); LevelUpChance = config.Bind<float>("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<int>("2 - Breeding", "Max Breeding Level", 3, SyncedConfig("Maximum level (3 = 2 stars) a Dvergr can reach through breeding.")); MaxFollowLeash = config.Bind<float>("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<int>("3 - Economy", "Contract Cost", 999, SyncedConfig("Cost in coins to purchase a Dvergr Contract from Haldor or Recruiter.")); AllyHealthMultiplier = config.Bind<float>("4 - Stats", "Health Multiplier", 1f, SyncedConfig("Multiplier applied to base health of Ally Dvergrs.")); AllyDamageMultiplier = config.Bind<float>("4 - Stats", "Damage Multiplier", 1f, SyncedConfig("Multiplier applied to base damage of Ally Dvergrs.")); EnableDebugLogs = config.Bind<bool>("5 - Debug", "Enable Debug Logs", false, SyncedConfig("Master switch to enable debug logging.")); DebugFull = config.Bind<bool>("5 - Debug", "Debug Full", false, SyncedConfig("If true, overrides sub-categories and logs absolutely everything.")); DebugAI = config.Bind<bool>("5 - Debug", "Debug AI", false, SyncedConfig("Log AI behavior overrides, targeting, and behavior resets.")); DebugBreeding = config.Bind<bool>("5 - Debug", "Debug Breeding", false, SyncedConfig("Log partner finding, pregnancy, birth, and genetic scaling.")); DebugEquipment = config.Bind<bool>("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<Projectile>(); 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<Projectile>(); 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<Character>(); ((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<SE_Stats>(); ((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<ItemData> m_elementalStaves = new List<ItemData>(); private int m_elementSetIndex; private void Awake() { m_monsterAI = ((Component)this).GetComponent<MonsterAI>(); m_character = ((Component)this).GetComponent<Character>(); m_isConfigured = false; } 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<Character>(); 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<GameObject>(prefab, ((Component)this).transform.position, Quaternion.identity); } GameObject prefab2 = ZNetScene.instance.GetPrefab("vfx_creature_soothed"); if ((Object)(object)prefab2 != (Object)null) { Object.Instantiate<GameObject>(prefab2, ((Component)this).transform.position + Vector3.up, Quaternion.identity); } } } DvergrHoT[] components = ((Component)component).gameObject.GetComponents<DvergrHoT>(); for (int j = 0; j < components.Length; j++) { Object.Destroy((Object)(object)components[j]); } ((Component)component).gameObject.AddComponent<DvergrHoT>().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<ItemDrop>().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<ItemData> list = new List<ItemData>(); 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<ItemData> list = new List<ItemData>(); 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<ZNetView>(); m_humanoid = ((Component)this).GetComponent<Humanoid>(); 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<DvergrProcreation>(); if ((Object)(object)component != (Object)null && component.IsPregnant()) { int pregnancyTimeLeft = component.GetPregnancyTimeLeft(); string text2 = ((pregnancyTimeLeft > 0) ? $"{pregnancyTimeLeft}s" : "Imminent"); text = text + "\n<color=yellow>Pregnant: " + text2 + "</color>"; } DvergrTameable component2 = ((Component)this).GetComponent<DvergrTameable>(); if ((Object)(object)component2 != (Object)null && ((Tameable)component2).IsHungry()) { text += "\n<color=red>Hungry</color>"; } 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<Character>(); 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<GameObject>(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<ZNetView>(); m_character = ((Component)this).GetComponent<Character>(); m_baseAI = ((Component)this).GetComponent<BaseAI>(); m_genetics = ((Component)this).GetComponent<DvergrGenetics>(); 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<DvergrTameable>(); 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<Character>(); if ((Object)(object)component != (Object)null && component.IsTamed() && (Object)(object)((Component)component).GetComponent<DvergrGenetics>() != (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<Character>(); if (!((Object)(object)component2 != (Object)null) || !((Object)(object)((Component)component2).gameObject != (Object)(object)((Component)this).gameObject)) { continue; } DvergrGenetics component3 = ((Component)component2).GetComponent<DvergrGenetics>(); if (!((Object)(object)component3 != (Object)null) || !((Object)(object)m_genetics != (Object)null) || m_genetics.GetGender() == component3.GetGender()) { continue; } DvergrProcreation component4 = ((Component)component2).GetComponent<DvergrProcreation>(); 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<GameObject>(prefab, val, Quaternion.LookRotation(((Component)this).transform.forward)).GetComponent<Character>(); 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<DvergrProcreation>(); if ((Object)(object)component != (Object)null && component.IsPregnant()) { int pregnancyTimeLeft = component.GetPregnancyTimeLeft(); string text2 = ((pregnancyTimeLeft > 0) ? $"{pregnancyTimeLeft}s" : "Imminent"); text = text + "\n<color=yellow>Pregnant: " + text2 + "</color>"; } if (((Tameable)this).IsHungry()) { text += "\n<color=red>Hungry</color>"; } 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<Humanoid>(); m_character = ((Component)this).GetComponent<Character>(); m_nview = ((Component)this).GetComponent<ZNetView>(); 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<string, string> dictionary = new Dictionary<string, string>(); 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<ItemData> allItems = m_humanoid.GetInventory().GetAllItems(); List<int> list = new List<int>(); List<string> list2 = new List<string>(); 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<GameObject> list3 = new List<GameObject>(); 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<GameObject> list4 = new List<GameObject>(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.0")] [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.0"; private readonly Harmony harmony = new Harmony("wubarrk.dvergrallies"); private void Awake() { 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.0 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<SummonDvergrStatusEffect>(); ((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<string> list = new List<string>(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<GameObject>(prefab, val, ((Component)character).transform.rotation).GetComponent<Tameable>(); 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<ItemDrop>(), 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.0"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }