// Consolidated decompiled source — Wubarrk-DvergrAllies v1.0.3 // Generated by Hexium's decompiled-source browser. Best-effort concatenation of every type in this // version's manifest — decompiler output isn't guaranteed to compile as-is. using System; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis; using System.Collections.Generic; using Jotunn; using Jotunn.Entities; using Jotunn.Managers; using UnityEngine; using HarmonyLib; using System.Reflection; using BepInEx.Configuration; using System.Collections; using BepInEx; using BepInEx.Bootstrap; using Jotunn.Utils; // ---- DvergrAllies/plugins/DvergrAllies.dll :: Microsoft.CodeAnalysis.EmbeddedAttribute ---- namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: System.Runtime.CompilerServices.RefSafetyRulesAttribute ---- namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.CustomDvergrDef ---- 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; } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.AllyPrefabManager ---- namespace DvergrAllies { 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 (prefab == null) { return; } Tameable component = prefab.GetComponent(); if (component != null) { Object.DestroyImmediate(component); } DvergrTameable dvergrTameable = prefab.GetComponent(); if (dvergrTameable == null) { dvergrTameable = prefab.AddComponent(); } dvergrTameable.m_tamingTime = ConfigManager.TamingTime.Value; dvergrTameable.m_fedDuration = ConfigManager.FedDuration.Value; dvergrTameable.m_commandable = true; Procreation component2 = prefab.GetComponent(); if (component2 != null) { Object.DestroyImmediate(component2); } if (prefab.GetComponent() == null) { prefab.AddComponent(); } if (prefab.GetComponent() == null) { prefab.AddComponent(); } MonsterAI component3 = prefab.GetComponent(); if (!(component3 != null)) { return; } component3.m_passiveAggresive = false; component3.m_fleeIfNotAlerted = false; component3.m_fleeIfLowHealth = 0f; component3.m_attackPlayerObjects = false; 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 (prefab2 != null) { ItemDrop component4 = prefab2.GetComponent(); if (component4 != 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 (prefab != null) { return prefab; } if (PrefabManager.Instance.GetPrefab(text) == null) { return null; } GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab(itemName, text); CustomItem val = new CustomItem(gameObject, true); ItemDrop itemDrop = val.ItemDrop; if (itemDrop != 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 (prefab2 != null) { ItemDrop component = prefab2.GetComponent(); if (component != 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 gameObject; } 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 (prefab == null) { return; } Humanoid component = prefab.GetComponent(); if (component == null) { return; } component.m_randomSets = new Humanoid.ItemSet[0]; component.m_randomWeapon = new GameObject[0]; component.m_randomShield = new GameObject[0]; if (def.StripArmor) { component.m_randomArmor = 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 (prefab2 != 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 (orCloneWeaponWithMeleeAnim != 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 (component2 != 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 if (PrefabManager.Instance.GetPrefab(basePrefabName) == null) { Logger.LogWarning((object)("Base prefab " + basePrefabName + " not found!")); return; } GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab(newPrefabName, basePrefabName); if (gameObject == null) { return; } CustomPrefab val = new CustomPrefab(gameObject, true); StripBalrondComponents(gameObject); Tameable component = gameObject.GetComponent(); if (component != null) { Object.DestroyImmediate(component); } DvergrTameable dvergrTameable = gameObject.GetComponent(); if (dvergrTameable == null) { dvergrTameable = gameObject.AddComponent(); } dvergrTameable.m_tamingTime = ConfigManager.TamingTime.Value; dvergrTameable.m_fedDuration = ConfigManager.FedDuration.Value; dvergrTameable.m_commandable = true; MonsterAI component2 = gameObject.GetComponent(); if (component2 != 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 (prefab != null) { ItemDrop component3 = prefab.GetComponent(); if (component3 != 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; component2.m_aggravatable = false; component2.m_passiveAggresive = false; component2.m_fleeIfLowHealth = 0f; component2.m_fleeIfNotAlerted = false; component2.m_circulateWhileCharging = false; component2.m_circulateWhileChargingFlying = false; component2.m_avoidFire = false; 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 = gameObject.GetComponent(); if (component4 != null) { Object.DestroyImmediate(component4); } if (gameObject.GetComponent() == null) { gameObject.AddComponent(); } gameObject.AddComponent(); gameObject.AddComponent(); DvergrCombatAI dvergrCombatAI = gameObject.AddComponent(); dvergrCombatAI.IsMeleeClass = isMelee; dvergrCombatAI.IsRangedClass = isRanged; dvergrCombatAI.IsClericClass = isCleric; Character component5 = gameObject.GetComponent(); if (component5 != null) { component5.m_health *= ConfigManager.AllyHealthMultiplier.Value * healthMultiplier; component5.m_faction = Character.Faction.Players; component5.m_runSpeed = Mathf.Max(component5.m_runSpeed, 6.5f); component5.m_walkSpeed = Mathf.Max(component5.m_walkSpeed, 3f); } Humanoid component6 = gameObject.GetComponent(); if (component6 != null) { component6.m_name = displayName; } PrefabManager.Instance.AddPrefab(val); } 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 (component != null) { Object.DestroyImmediate(component); Logger.LogInfo((object)("[DvergrAllies] Stripped " + text + " from cloned " + prefab.name)); ConfigManager.LogDebug("[Compat] Removed " + text + " from " + prefab.name + " to prevent Balrond Idle Actors conflict."); } } } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.Patch_ZNetScene_Awake_Dvergrs ---- namespace DvergrAllies { [HarmonyPatch(typeof(ZNetScene), "Awake")] public static class Patch_ZNetScene_Awake_Dvergrs { [HarmonyPostfix] public static void Postfix(ZNetScene __instance) { if (__instance == null || __instance.m_prefabs == null) { return; } if (Plugin.HasBalrondIdleActors) { Debug.LogError("======================================================================="); Debug.LogError("[DvergrAllies] BalrondIdleActors detected! Skipping wild Dvergr injection!"); Debug.LogError("[DvergrAllies] Enabling compatibility mode: Wild Dvergr modifications are disabled."); Debug.LogError("======================================================================="); return; } Logger.LogInfo((object)"Scanning ZNetScene for wild Dvergr prefabs to make tamable..."); int num = 0; foreach (GameObject prefab in __instance.m_prefabs) { if (!(prefab == null)) { string text = prefab.name.ToLower(); if ((text.Contains("dverg") || text.Contains("dverger")) && !text.Contains("ally") && prefab.GetComponent() != null && prefab.GetComponent() != null) { AllyPrefabManager.MakePrefabTamable(prefab); num++; } } } Logger.LogInfo((object)$"Successfully injected Taming/Breeding components into {num} wild Dvergr prefabs!"); } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.Patch_MonsterAI_UpdateAI_Dvergrs ---- namespace DvergrAllies { [HarmonyPatch(typeof(MonsterAI), "UpdateAI")] public static class Patch_MonsterAI_UpdateAI_Dvergrs { [HarmonyPrefix] public static void Prefix(MonsterAI __instance) { if (!(__instance == null) && !(__instance.gameObject == null)) { string text = __instance.gameObject.name.ToLower(); if ((text.Contains("dverg") || text.Contains("dverger")) && !text.Contains("ally") && !Plugin.HasBalrondIdleActors) { __instance.m_passiveAggresive = false; __instance.m_attackPlayerObjects = false; __instance.m_avoidFire = false; __instance.m_fleeIfNotAlerted = false; } } } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.CheckMethods ---- namespace DvergrAllies { public class CheckMethods { public static void Dump() { MethodInfo methodInfo = AccessTools.Method(typeof(MonsterAI), "Awake"); Logger.LogInfo((object)("MonsterAI Awake is " + ((methodInfo == null) ? "NULL" : methodInfo.DeclaringType.Name))); methodInfo = AccessTools.Method(typeof(BaseAI), "Awake"); Logger.LogInfo((object)("BaseAI Awake is " + ((methodInfo == null) ? "NULL" : methodInfo.DeclaringType.Name))); } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.ConfigManager ---- namespace DvergrAllies { 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 return new ConfigDescription(description, null, (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", defaultValue: false, SyncedConfig("Master switch to enable debug logging.")); DebugFull = config.Bind("5 - Debug", "Debug Full", defaultValue: false, SyncedConfig("If true, overrides sub-categories and logs absolutely everything.")); DebugAI = config.Bind("5 - Debug", "Debug AI", defaultValue: false, SyncedConfig("Log AI behavior overrides, targeting, and behavior resets.")); DebugBreeding = config.Bind("5 - Debug", "Debug Breeding", defaultValue: false, SyncedConfig("Log partner finding, pregnancy, birth, and genetic scaling.")); DebugEquipment = config.Bind("5 - Debug", "Debug Equipment", defaultValue: false, SyncedConfig("Log prefab creation, loadout setup, and weapon scaling/upgrades.")); } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.CustomStavesManager ---- namespace DvergrAllies { 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 if (PrefabManager.Instance.GetPrefab("DvergerStaffFire") == null) { return; } CustomItem val = new CustomItem("AllyDvergrStaffEarth", "DvergerStaffFire"); ItemDrop itemDrop = val.ItemDrop; itemDrop.m_itemData.m_shared.m_name = "Earth Staff"; if (PrefabManager.Instance.GetPrefab("DvergerStaffIce_projectile") != null) { GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("AllyDvergrStaffEarth_projectile", "DvergerStaffIce_projectile"); Projectile component = gameObject.GetComponent(); if (component != 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 (prefab != null) { component.m_hitEffects.m_effectPrefabs = new EffectList.EffectData[1] { new EffectList.EffectData { m_prefab = prefab, m_enabled = true } }; } } itemDrop.m_itemData.m_shared.m_attack.m_attackProjectile = gameObject; } 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 (PrefabManager.Instance.GetPrefab("DvergerStaffFire") == null) { return; } CustomItem val = new CustomItem("AllyDvergrStaffSpirit", "DvergerStaffFire"); ItemDrop itemDrop = val.ItemDrop; itemDrop.m_itemData.m_shared.m_name = "Spirit Staff"; if (PrefabManager.Instance.GetPrefab("DvergerStaffIce_projectile") != null) { GameObject gameObject = PrefabManager.Instance.CreateClonedPrefab("AllyDvergrStaffSpirit_projectile", "DvergerStaffIce_projectile"); Projectile component = gameObject.GetComponent(); if (component != 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 = gameObject; } ItemManager.Instance.AddItem(val); } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.DummyCheck ---- namespace DvergrAllies { public class DummyCheck { public void Check() { } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.DvergrBerserkerRage ---- namespace DvergrAllies { public class DvergrBerserkerRage : MonoBehaviour { private Character m_character; private void Start() { m_character = GetComponent(); InvokeRepeating("ApplyRage", 1f, 5f); } private void ApplyRage() { if (!(m_character == null) && !m_character.IsDead()) { SEMan sEMan = m_character.GetSEMan(); if (sEMan != null && !sEMan.HaveStatusEffect("BerserkerRage".GetStableHashCode())) { SE_Stats sE_Stats = ScriptableObject.CreateInstance(); sE_Stats.name = "BerserkerRage"; sE_Stats.m_name = "Rage"; sE_Stats.m_speedModifier = 1.15f; sE_Stats.m_staminaRegenMultiplier = 1.3f; sE_Stats.m_healthRegenMultiplier = 1.2f; sE_Stats.m_raiseSkill = Skills.SkillType.Axes; sE_Stats.m_raiseSkillModifier = 15f; sEMan.AddStatusEffect(sE_Stats); } } } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.DvergrCombatAI ---- namespace DvergrAllies { 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 = GetComponent(); m_character = GetComponent(); m_isConfigured = false; if (!Plugin.HasBalrondIdleActors) { return; } string[] array = new string[3] { "BalrondActor", "BalrondActorTalker", "BalrondActorNamePicker" }; foreach (string text in array) { Component component = GetComponent(text); if (component != null) { Object.Destroy(component); ConfigManager.LogDebug("[Compat] Runtime stripped " + text + " from existing active " + base.gameObject.name); } } } private void Start() { ForceAggressiveAI(); Invoke("ForceAggressiveAI", 1f); Invoke("ForceAggressiveAI", 3f); Invoke("InjectMagicWeapons", 0.5f); } private void ForceAggressiveAI() { if (!(m_monsterAI == null)) { m_monsterAI.m_aggravatable = false; m_monsterAI.m_passiveAggresive = false; m_monsterAI.m_fleeIfNotAlerted = false; m_monsterAI.m_avoidFire = false; 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; m_monsterAI.m_randomMoveInterval = 0f; m_monsterAI.m_randomCircleInterval = 0f; m_monsterAI.m_alertRange = 30f; } else if (IsRangedClass) { m_monsterAI.m_circulateWhileCharging = false; m_monsterAI.m_circulateWhileChargingFlying = false; m_monsterAI.m_randomMoveInterval = 10f; } else if (IsClericClass) { m_monsterAI.m_circulateWhileCharging = false; m_monsterAI.m_randomMoveInterval = 5f; m_monsterAI.m_randomCircleInterval = 5f; m_monsterAI.m_alertRange = 30f; } if (!m_initialized) { m_initialized = true; ConfigManager.LogAI($"[CombatAI] Forced aggressive AI on {base.gameObject.name} (Melee:{IsMeleeClass}, Ranged:{IsRangedClass})"); } } } private void FixedUpdate() { if (m_monsterAI == null) { return; } if (Time.frameCount % 60 == 0 && (m_monsterAI.m_aggravatable || m_monsterAI.m_passiveAggresive || m_monsterAI.m_fleeIfNotAlerted)) { ConfigManager.LogAI("[CombatAI] AI flags were reset on " + base.gameObject.name + ", re-forcing!"); ForceAggressiveAI(); } if (IsMeleeClass || IsClericClass) { m_monsterAI.m_randomCircleInterval = 9999f; m_monsterAI.m_circulateWhileCharging = false; } GameObject followTarget = m_monsterAI.GetFollowTarget(); if (followTarget != null && m_monsterAI.GetTargetCreature() != null) { float num = Vector3.Distance(base.transform.position, followTarget.transform.position); if (num > ConfigManager.MaxFollowLeash.Value) { m_monsterAI.SetTarget((Character)null); ConfigManager.LogAI($"[Leash] {base.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() { if ((((BaseAI)m_monsterAI).m_character != 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(base.transform.position, 15f, LayerMask.GetMask("character", "character_net", "character_ghost")); for (int i = 0; i < array.Length; i++) { Character component = array[i].GetComponent(); if (!(component != null) || component.IsDead() || (!(component == ((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 (ZNetScene.instance != null) { GameObject prefab = ZNetScene.instance.GetPrefab("vfx_Potion_health_medium"); if (prefab != null) { Object.Instantiate(prefab, base.transform.position, Quaternion.identity); } GameObject prefab2 = ZNetScene.instance.GetPrefab("vfx_creature_soothed"); if (prefab2 != null) { Object.Instantiate(prefab2, base.transform.position + Vector3.up, Quaternion.identity); } } } DvergrHoT[] components = component.gameObject.GetComponents(); for (int j = 0; j < components.Length; j++) { Object.Destroy(components[j]); } component.gameObject.AddComponent().Setup(level); } } private ItemDrop.ItemData CreateNativeVariant(string baseWeaponName, string newProjectileName = null) { if (ObjectDB.instance == null || ZNetScene.instance == null) { return null; } GameObject gameObject = ObjectDB.instance.GetItemPrefab(baseWeaponName) ?? ZNetScene.instance.GetPrefab(baseWeaponName); if (gameObject == null) { ConfigManager.LogAI("[NativeVariant] Base weapon " + baseWeaponName + " not found!"); return null; } ItemDrop.ItemData itemData = gameObject.GetComponent().m_itemData.Clone(); itemData.m_dropPrefab = gameObject; ItemDrop.ItemData.SharedData shared = itemData.m_shared; itemData.m_shared = new ItemDrop.ItemData.SharedData(); FieldInfo[] fields = typeof(ItemDrop.ItemData.SharedData).GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(itemData.m_shared, fieldInfo.GetValue(shared)); } itemData.m_shared.m_attack = shared.m_attack.Clone(); if (shared.m_secondaryAttack != null) { itemData.m_shared.m_secondaryAttack = shared.m_secondaryAttack.Clone(); } itemData.m_shared.m_attack.m_attackStamina = 0f; itemData.m_shared.m_attack.m_attackHealth = 0f; itemData.m_shared.m_attack.m_attackEitr = 0f; float value = ConfigManager.AllyDamageMultiplier.Value; if (value != 1f) { itemData.m_shared.m_damages.m_damage *= value; itemData.m_shared.m_damages.m_blunt *= value; itemData.m_shared.m_damages.m_slash *= value; itemData.m_shared.m_damages.m_pierce *= value; itemData.m_shared.m_damages.m_chop *= value; itemData.m_shared.m_damages.m_pickaxe *= value; itemData.m_shared.m_damages.m_fire *= value; itemData.m_shared.m_damages.m_frost *= value; itemData.m_shared.m_damages.m_lightning *= value; itemData.m_shared.m_damages.m_poison *= value; itemData.m_shared.m_damages.m_spirit *= value; } if (newProjectileName != null) { GameObject gameObject2 = ObjectDB.instance.GetItemPrefab(newProjectileName) ?? ZNetScene.instance.GetPrefab(newProjectileName); if (gameObject2 != null) { itemData.m_shared.m_attack.m_attackProjectile = gameObject2; } } return itemData; } private void InjectMagicWeapons() { if (m_monsterAI == null || ((BaseAI)m_monsterAI).m_character == null) { return; } Humanoid humanoid = ((BaseAI)m_monsterAI).m_character as Humanoid; if (humanoid == null || humanoid.GetInventory() == null) { return; } ConfigManager.LogAI($"[RuntimeBuilder] {base.gameObject.name} - inventory has {humanoid.GetInventory().GetAllItems().Count} items"); foreach (ItemDrop.ItemData allItem in humanoid.GetInventory().GetAllItems()) { if (allItem.IsWeapon() && allItem.m_dropPrefab != null && allItem.m_dropPrefab.name.Contains("Heal")) { allItem.m_shared.m_aiAttackInterval = 30f; ConfigManager.LogAI("[RuntimeBuilder] NERFED heal staff: " + allItem.m_dropPrefab.name + " -> interval=30s"); } } if (base.gameObject.name.Contains("SpellswordFire")) { ItemDrop.ItemData itemData = CreateNativeVariant("DvergerStaffFire_fireball"); if (itemData != null) { itemData.m_shared.m_aiAttackInterval = 15f; humanoid.GetInventory().AddItem(itemData); ConfigManager.LogAI("[SpellswordFire] Injected Native Fireball into memory inventory!"); } } else if (base.gameObject.name.Contains("SpellswordIce")) { ItemDrop.ItemData itemData2 = CreateNativeVariant("DvergerStaffIce_icebolt"); if (itemData2 != null) { itemData2.m_shared.m_aiAttackInterval = 15f; humanoid.GetInventory().AddItem(itemData2); 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((ItemDrop.ItemData x) => x == null); List list = new List(); foreach (ItemDrop.ItemData allItem2 in humanoid.GetInventory().GetAllItems()) { if (allItem2.IsWeapon()) { humanoid.UnequipItem(allItem2); list.Add(allItem2); } } foreach (ItemDrop.ItemData item in list) { humanoid.GetInventory().RemoveItem(item); } if (m_elementalStaves.Count > 0) { humanoid.GetInventory().AddItem(m_elementalStaves[0]); humanoid.EquipItem(m_elementalStaves[0]); } ConfigManager.LogAI($"[ElementalMage] Loaded {m_elementalStaves.Count} elements into memory cache."); m_elementRotateTimer = 0f; } private void UpdateElementalMageAI() { if (((BaseAI)m_monsterAI).m_character == null) { return; } Humanoid humanoid = ((BaseAI)m_monsterAI).m_character as Humanoid; if (humanoid == null) { return; } Inventory inventory = humanoid.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; ItemDrop.ItemData item = m_elementalStaves[m_elementSetIndex]; List list = new List(); foreach (ItemDrop.ItemData allItem in inventory.GetAllItems()) { if (allItem.IsWeapon()) { humanoid.UnequipItem(allItem); list.Add(allItem); } } foreach (ItemDrop.ItemData item2 in list) { inventory.RemoveItem(item2); } inventory.AddItem(item); humanoid.EquipItem(item); ConfigManager.LogAI($"[ElementalMage] Swapped to element {m_elementSetIndex}"); } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.DvergrGenetics ---- namespace DvergrAllies { public class DvergrGenetics : MonoBehaviour { public enum Gender { Male, Female } public const string StealthExemptKey = "SoMStealthExempt"; private ZNetView m_nview; private Humanoid m_humanoid; public static void RefreshStealthExemption(Character c, ZNetView nview) { if (!(c == null)) { RefreshStealthExemption(c, nview, c.IsTamed()); } } public static void RefreshStealthExemption(Character c, ZNetView nview, bool tamed) { if (!(c == null) && !(nview == null) && nview.IsValid()) { ZDO zDO = nview.GetZDO(); if (zDO != null) { bool flag = tamed || c.m_faction == Character.Faction.Players; zDO.Set("SoMStealthExempt", flag ? 1 : 0); } } } private void Awake() { m_nview = GetComponent(); m_humanoid = GetComponent(); if (!(m_nview == null) && m_nview.IsValid() && m_nview.IsOwner()) { RefreshStealthExemption(GetComponent(), m_nview); if (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] {base.gameObject.name} randomly assigned gender: {gender}"); } } } private void LateUpdate() { if (m_nview == null || !m_nview.IsValid() || !(Player.m_localPlayer != null) || !(Player.m_localPlayer.GetHoverObject() != null)) { return; } GameObject hoverObject = Player.m_localPlayer.GetHoverObject(); if ((!(hoverObject == base.gameObject) && !hoverObject.transform.IsChildOf(base.transform)) || !(Hud.instance != null) || !((Object)(object)Hud.instance.m_hoverName != null)) { return; } int num = m_nview.GetZDO().GetInt("dvergr_gender", -1); if (num != -1) { Gender gender = (Gender)num; string text = $"\n({gender})"; DvergrProcreation component = GetComponent(); if (component != null && component.IsPregnant()) { int pregnancyTimeLeft = component.GetPregnancyTimeLeft(); string text2 = ((pregnancyTimeLeft > 0) ? $"{pregnancyTimeLeft}s" : "Imminent"); text = text + "\nPregnant: " + text2 + ""; } DvergrTameable component2 = GetComponent(); if (component2 != null && component2.IsHungry()) { text += "\nHungry"; } Hud.instance.m_hoverName.text += text; } } public Gender GetGender() { if (m_nview == null || !m_nview.IsValid()) { return Gender.Male; } return (Gender)m_nview.GetZDO().GetInt("dvergr_gender"); } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.Patch_Character_SetTamed_StealthExemption ---- namespace DvergrAllies { [HarmonyPatch(typeof(Character), "SetTamed")] public static class Patch_Character_SetTamed_StealthExemption { [HarmonyPostfix] public static void Postfix(Character __instance, bool tamed) { if (!(__instance == null) && !(__instance.GetComponent() == null)) { ZNetView component = __instance.GetComponent(); if (!(component == null) && component.IsValid() && component.IsOwner()) { DvergrGenetics.RefreshStealthExemption(__instance, component, tamed); } } } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.DvergrHoT ---- namespace DvergrAllies { 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 = GetComponent(); m_healAmountPerTick = 5f + 5f * (float)clericLevel; m_ticksRemaining = 5; m_tickTimer = 1f; ConfigManager.LogAI($"[Cleric] Applied HoT to {m_target?.name} (Lvl {clericLevel}): {m_healAmountPerTick} HP/s for {m_ticksRemaining}s"); } private void FixedUpdate() { if (m_target == null || m_target.IsDead()) { Object.Destroy(this); return; } m_tickTimer += Time.fixedDeltaTime; if (!(m_tickTimer >= 1f)) { return; } m_tickTimer = 0f; m_ticksRemaining--; m_target.Heal(m_healAmountPerTick); if (ZNetScene.instance != null) { GameObject prefab = ZNetScene.instance.GetPrefab("vfx_Potion_health_medium"); if (prefab != null) { Object.Instantiate(prefab, m_target.transform.position, Quaternion.identity); } } if (m_ticksRemaining <= 0) { Object.Destroy(this); } } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.DvergrProcreation ---- namespace DvergrAllies { 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 = GetComponent(); m_character = GetComponent(); m_baseAI = GetComponent(); m_genetics = GetComponent(); if (!(m_nview == null) && m_nview.IsValid()) { InvokeRepeating("Procreate", UnityEngine.Random.Range(0f, ConfigManager.UpdateInterval.Value), ConfigManager.UpdateInterval.Value); } } public bool IsPregnant() { if (m_nview != 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 b = Mathf.CeilToInt((float)((double)ConfigManager.PregnancyDuration.Value - totalSeconds)); return Mathf.Max(0, b); } private void MakePregnant(string partnerPrefab, int partnerLevel) { if (!(m_nview == 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 (!(m_nview == 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 (m_character == null || !m_character.IsTamed()) { return false; } if (m_baseAI != null && m_baseAI.IsAlerted()) { return false; } DvergrTameable component = GetComponent(); if (component != null && component.IsHungry()) { return false; } return true; } private void Procreate() { 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 = StartCoroutine(BirthingProcess()); } } } else { if (!ReadyForProcreation()) { return; } string text = base.gameObject.name.Replace("(Clone)", "").Trim(); int num2 = 0; Collider[] array = Physics.OverlapSphere(base.transform.position, 10f, LayerMask.GetMask("character", "character_net", "character_ghost")); for (int i = 0; i < array.Length; i++) { Character component = array[i].GetComponent(); if (component != null && component.IsTamed() && component.GetComponent() != 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(base.transform.position, ConfigManager.PartnerCheckRange.Value, LayerMask.GetMask("character", "character_net", "character_ghost")); for (int i = 0; i < array.Length; i++) { Character component2 = array[i].GetComponent(); if (!(component2 != null) || !(component2.gameObject != base.gameObject)) { continue; } DvergrGenetics component3 = component2.GetComponent(); if (!(component3 != null) || !(m_genetics != null) || m_genetics.GetGender() == component3.GetGender()) { continue; } DvergrProcreation component4 = component2.GetComponent(); if (!(component4 != null) || !component4.ReadyForProcreation()) { continue; } ConfigManager.LogBreeding($"[Procreation] {text} ({m_genetics.GetGender()}) found compatible partner {component2.gameObject.name} ({component3.GetGender()})"); if (UnityEngine.Random.value <= ConfigManager.PregnancyChance.Value) { string text2 = 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 = base.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 = base.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 (prefab != null) { Vector3 position = base.transform.position + base.transform.forward * m_spawnOffset; Character component = UnityEngine.Object.Instantiate(prefab, position, Quaternion.LookRotation(base.transform.forward)).GetComponent(); if (component != null && m_character != null) { component.SetTamed(m_character.IsTamed()); int num2 = Mathf.Max(m_character.GetLevel(), num); if ((float)UnityEngine.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(base.transform.position, Quaternion.identity); } } 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)UnityEngine.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 (!(UnityEngine.Random.value > 0.5f)) { return parentB; } return parentA; } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.DvergrTameable ---- namespace DvergrAllies { public class DvergrTameable : Tameable, Hoverable { public new string GetHoverText() { string text = base.GetHoverText(); DvergrProcreation component = GetComponent(); if (component != null && component.IsPregnant()) { int pregnancyTimeLeft = component.GetPregnancyTimeLeft(); string text2 = ((pregnancyTimeLeft > 0) ? $"{pregnancyTimeLeft}s" : "Imminent"); text = text + "\nPregnant: " + text2 + ""; } if (IsHungry()) { text += "\nHungry"; } return text; } public new string GetHoverName() { return base.GetHoverName(); } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.DvergrWeaponScaler ---- namespace DvergrAllies { 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 = GetComponent(); m_character = GetComponent(); m_nview = GetComponent(); if (m_nview != null && m_nview.IsValid() && m_nview.IsOwner()) { Invoke("DoScaleWeapons", 1f); } } private void DoScaleWeapons() { if (m_scaled || m_humanoid == null || m_character == null) { return; } m_scaled = true; int level = m_character.GetLevel(); if (level <= 1) { return; } string prefabName = Utils.GetPrefabName(base.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"; } ItemDrop.ItemData rightItem = m_humanoid.GetRightItem(); if (rightItem != null) { m_humanoid.UnequipItem(rightItem); } ItemDrop.ItemData leftItem = m_humanoid.GetLeftItem(); if (leftItem != null) { m_humanoid.UnequipItem(leftItem); } List allItems = m_humanoid.GetInventory().GetAllItems(); List list = new List(); List list2 = new List(); for (int i = 0; i < allItems.Count; i++) { ItemDrop.ItemData itemData = allItems[i]; string text = ((itemData.m_dropPrefab != null) ? itemData.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 (orCloneWeaponWithMeleeAnim != null) { list3.Add(orCloneWeaponWithMeleeAnim); bool flag = m_humanoid.GetInventory().AddItem(orCloneWeaponWithMeleeAnim, 1); ConfigManager.LogEquipment($"[WeaponScaler] Added {item} to inventory: {flag}"); if (!flag) { continue; } foreach (ItemDrop.ItemData allItem in m_humanoid.GetInventory().GetAllItems()) { if (allItem.m_dropPrefab != null && allItem.m_dropPrefab.name == item) { m_humanoid.EquipItem(allItem); 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(); } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.Plugin ---- namespace DvergrAllies { [BepInPlugin("wubarrk.dvergrallies", "DvergrAllies", "1.0.3")] [BepInDependency("com.jotunn.jotunn", BepInDependency.DependencyFlags.HardDependency)] [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.3"; public static bool HasBalrondIdleActors; private readonly Harmony harmony = new Harmony("wubarrk.dvergrallies"); private void Awake() { HasBalrondIdleActors = Chainloader.PluginInfos.ContainsKey("balrond.astafaraios.BalrondIdleActors"); ConfigManager.Init(base.Config); PrefabManager.OnVanillaPrefabsAvailable += CustomStavesManager.Setup; PrefabManager.OnVanillaPrefabsAvailable += AllyPrefabManager.Setup; PrefabManager.OnVanillaPrefabsAvailable += RecruiterManager.Setup; harmony.PatchAll(); base.Logger.LogInfo("DvergrAllies v1.0.3 has loaded!"); } private void OnDestroy() { harmony.UnpatchSelf(); } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.RecruiterManager ---- namespace DvergrAllies { 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown if (!(PrefabManager.Instance.GetPrefab("Amber") == 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 = ItemDrop.ItemData.ItemType.Consumable; SummonDvergrStatusEffect summonDvergrStatusEffect = ScriptableObject.CreateInstance(); summonDvergrStatusEffect.name = "SE_SummonDvergr"; summonDvergrStatusEffect.m_name = "Summoning"; summonDvergrStatusEffect.m_ttl = 0.1f; ItemManager.Instance.AddStatusEffect(new CustomStatusEffect((StatusEffect)summonDvergrStatusEffect, false)); itemDrop.m_itemData.m_shared.m_consumeStatusEffect = 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); } } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.SummonDvergrStatusEffect ---- namespace DvergrAllies { public class SummonDvergrStatusEffect : StatusEffect { private readonly string[] baseTypes = new string[5] { "AllyDvergrRogue", "AllyDvergrMage", "AllyDvergrMageFire", "AllyDvergrMageIce", "AllyDvergrMageSupport" }; public override void Setup(Character character) { base.Setup(character); if (!(character != 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 (prefab != null) { Vector3 position = character.transform.position + character.transform.forward * 2f; Tameable component = Object.Instantiate(prefab, position, character.transform.rotation).GetComponent(); if (component != null) { component.Tame(); } string text2 = text.Replace("AllyDvergr", ""); if (text2 == "Rogue") { text2 = "Rogue"; } character.Message(MessageHud.MessageType.Center, "Summoned an Ally Dvergr " + text2 + "!"); } } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.Patches_Trader ---- namespace DvergrAllies { [HarmonyPatch(typeof(Trader))] public static class Patches_Trader { [HarmonyPatch("Start")] [HarmonyPostfix] public static void TraderStartPostfix(Trader __instance) { if (!__instance.gameObject.name.Contains("Haldor")) { return; } GameObject prefab = PrefabManager.Instance.GetPrefab("DvergrContract"); if (!(prefab != null)) { return; } foreach (Trader.TradeItem item in __instance.m_items) { if (item.m_prefab != null && item.m_prefab.gameObject.name == "DvergrContract") { return; } } Trader.TradeItem tradeItem = new Trader.TradeItem { m_prefab = prefab.GetComponent(), m_price = ConfigManager.ContractCost.Value, m_stack = 1, m_requiredGlobalKey = "" }; FieldInfo[] fields = typeof(Trader.TradeItem).GetFields(); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(string) && fieldInfo.GetValue(tradeItem) == null) { fieldInfo.SetValue(tradeItem, ""); } } __instance.m_items.Add(tradeItem); } } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: DvergrAllies.PluginInfo ---- namespace DvergrAllies { public static class PluginInfo { public const string PLUGIN_GUID = "DvergrAllies"; public const string PLUGIN_NAME = "DvergrAllies"; public const string PLUGIN_VERSION = "1.0.3"; } } // ---- DvergrAllies/plugins/DvergrAllies.dll :: System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute ---- namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }