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 TWIG Defenders v0.4.0
BepInEx/plugins/TwigDefenders/TwigDefenders.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("TwigDefenders")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.4.0.0")] [assembly: AssemblyInformationalVersion("0.4.0")] [assembly: AssemblyProduct("TwigDefenders")] [assembly: AssemblyTitle("TwigDefenders")] [assembly: AssemblyVersion("0.4.0.0")] [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 TwigDefenders { internal static class DefenderPrefab { internal static void Register() { PrefabManager instance = PrefabManager.Instance; GameObject prefab = instance.GetPrefab("piece_TrainingDummy"); GameObject prefab2 = instance.GetPrefab("TrainingDummy_attack"); if (!Object.op_Implicit((Object)(object)prefab) || !Object.op_Implicit((Object)(object)prefab2) || !Object.op_Implicit((Object)(object)prefab.GetComponent<Humanoid>()) || !Object.op_Implicit((Object)(object)prefab.GetComponent<MonsterAI>()) || !Object.op_Implicit((Object)(object)prefab.GetComponent<Piece>()) || !Object.op_Implicit((Object)(object)prefab.GetComponent<ZNetView>()) || !Object.op_Implicit((Object)(object)prefab.GetComponent<ZSyncTransform>()) || !Object.op_Implicit((Object)(object)prefab.GetComponent<ZSyncAnimation>()) || !Object.op_Implicit((Object)(object)prefab2.GetComponent<ItemDrop>())) { throw new InvalidOperationException("Unsupported T.W.I.G. prefab layout. See TWIG-prefab-research.txt."); } foreach (DefenderTier item in DefenderTiers.All) { GameObject prefab3 = instance.GetPrefab(item.Station); if (!Object.op_Implicit((Object)(object)prefab3) || !Object.op_Implicit((Object)(object)prefab3.GetComponent<CraftingStation>())) { throw new InvalidOperationException("Missing crafting station for " + item.DisplayName + ": " + item.Station); } foreach (TierMaterial material in item.Materials) { GameObject prefab4 = instance.GetPrefab(material.Prefab); if (material.Amount <= 0 || !Object.op_Implicit((Object)(object)prefab4) || !Object.op_Implicit((Object)(object)prefab4.GetComponent<ItemDrop>())) { throw new InvalidOperationException("Invalid material for " + item.DisplayName + ": " + material.Prefab); } } } DefenderRangedCombat.RegisterProjectile(); foreach (DefenderTier item2 in DefenderTiers.All) { RegisterTier(item2, prefab, prefab2); } TwigDefenderPlugin.Log("Registered " + DefenderTiers.All.Count + " biome-material defender tiers."); } private static void RegisterTier(DefenderTier tier, GameObject original, GameObject originalAttack) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Expected O, but got Unknown //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Expected O, but got Unknown //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Expected O, but got Unknown //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Expected O, but got Unknown PrefabManager instance = PrefabManager.Instance; ItemData itemData = originalAttack.GetComponent<ItemDrop>().m_itemData; float totalDamage = ((DamageTypes)(ref itemData.m_shared.m_damages)).GetTotalDamage(); GameObject val = instance.CreateClonedPrefab(tier.AttackName, originalAttack); ItemDrop component = val.GetComponent<ItemDrop>(); component.m_itemData = itemData.Clone(); SharedData val2 = new SharedData(); FieldInfo[] fields = typeof(SharedData).GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(val2, fieldInfo.GetValue(itemData.m_shared)); } component.m_itemData.m_shared = val2; component.m_itemData.m_dropPrefab = val; val2.m_attack = itemData.m_shared.m_attack.Clone(); val2.m_secondaryAttack = itemData.m_shared.m_secondaryAttack.Clone(); val2.m_name = tier.DisplayName + " attack"; val2.m_damages = new DamageTypes { m_blunt = tier.Damage }; val2.m_attack.m_attackStamina = 0f; val2.m_attack.m_hitFriendly = false; val2.m_attack.m_hitThroughWalls = false; val2.m_attack.m_snowShovel = false; val2.m_attack.m_recoilPushback = 0f; val2.m_useDurability = false; if (val2 == itemData.m_shared || val2.m_attack == itemData.m_shared.m_attack || ((DamageTypes)(ref itemData.m_shared.m_damages)).GetTotalDamage() != totalDamage) { throw new InvalidOperationException("Attack isolation validation failed."); } if (!ItemManager.Instance.AddItem(new CustomItem(val, false))) { throw new InvalidOperationException("Failed to register " + tier.AttackName); } GameObject val3 = instance.CreateClonedPrefab(tier.PrefabName, original); Object.DestroyImmediate((Object)(object)val3.GetComponent<MonsterAI>()); val3.AddComponent<TwigDefenderAI>(); Humanoid component2 = val3.GetComponent<Humanoid>(); ((Character)component2).m_name = tier.DisplayName; ((Character)component2).m_faction = (Faction)0; ((Character)component2).m_aiSkipTarget = false; ((Character)component2).m_health = tier.Health; ((Character)component2).m_turnSpeed = 180f; ((Character)component2).m_runTurnSpeed = 180f; ((Character)component2).m_speed = (((Character)component2).m_walkSpeed = (((Character)component2).m_runSpeed = (((Character)component2).m_swimSpeed = 0f))); component2.m_defaultItems = (GameObject[])(object)new GameObject[2] { val, DefenderRangedCombat.RegisterWeapon(tier) }; val3.GetComponent<Rigidbody>().constraints = (RigidbodyConstraints)94; val3.GetComponent<ZNetView>().m_persistent = true; val3.GetComponent<ZSyncTransform>().m_syncRotation = true; RequirementConfig[] array = (RequirementConfig[])(object)new RequirementConfig[tier.Materials.Count]; for (int j = 0; j < tier.Materials.Count; j++) { array[j] = new RequirementConfig(tier.Materials[j].Prefab, tier.Materials[j].Amount, 0, true); } PieceConfig val4 = new PieceConfig(); val4.Name = tier.DisplayName; val4.Description = tier.Biome + " defender. Melee at close range; spear throws up to 30 m.\nHealth: " + tier.Health + " | Blunt: " + tier.Damage + " | Spear pierce: " + tier.Damage * 0.75f; val4.PieceTable = "Hammer"; val4.Category = "Misc"; val4.CraftingStation = tier.Station; val4.Requirements = array; PieceConfig val5 = val4; CustomPiece val6 = new CustomPiece(val3, false, val5); if (!PieceManager.Instance.AddPiece(val6)) { throw new InvalidOperationException("Failed to register " + tier.PrefabName); } TwigDefenderPlugin.Log("Registered " + tier.PrefabName + "; HP=" + tier.Health + "; blunt=" + tier.Damage + "; station=" + tier.Station + "; separate attack data verified; vanilla damage=" + totalDamage); } } internal static class DefenderRangedCombat { internal const float Range = 30f; internal const float Speed = 35f; internal const float DamageScale = 0.75f; internal const string ProjectileName = "TWIGDefender_spear_projectile"; internal static void RegisterProjectile() { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown GameObject prefab = PrefabManager.Instance.GetPrefab("GoblinSpear_projectile"); if (!Object.op_Implicit((Object)(object)prefab) || !Object.op_Implicit((Object)(object)prefab.GetComponent<Projectile>()) || !Object.op_Implicit((Object)(object)prefab.GetComponent<ZNetView>())) { throw new InvalidOperationException("Fuling spear projectile layout is unsupported."); } GameObject val = PrefabManager.Instance.CreateClonedPrefab("TWIGDefender_spear_projectile", prefab); Projectile component = val.GetComponent<Projectile>(); component.m_hitFriendly = (component.m_hitOwner = false); component.m_noDamageFriendly = true; component.m_respawnItemOnHit = false; component.m_spawnOnHit = null; component.m_aoe = 0f; component.m_ttl = 5f; PrefabManager.Instance.AddPrefab(new CustomPrefab(val, false)); } internal static GameObject RegisterWeapon(DefenderTier tier) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Expected O, but got Unknown GameObject prefab = PrefabManager.Instance.GetPrefab("TrainingDummy_throw"); if (!Object.op_Implicit((Object)(object)prefab) || !Object.op_Implicit((Object)(object)prefab.GetComponent<ItemDrop>())) { throw new InvalidOperationException("Missing native dummy throw."); } GameObject val = PrefabManager.Instance.CreateClonedPrefab(tier.RangedAttackName, prefab); ItemData itemData = prefab.GetComponent<ItemDrop>().m_itemData; ItemDrop component = val.GetComponent<ItemDrop>(); component.m_itemData = itemData.Clone(); SharedData val2 = new SharedData(); FieldInfo[] fields = typeof(SharedData).GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(val2, fieldInfo.GetValue(itemData.m_shared)); } component.m_itemData.m_shared = val2; component.m_itemData.m_dropPrefab = val; val2.m_attack = itemData.m_shared.m_attack.Clone(); val2.m_secondaryAttack = itemData.m_shared.m_secondaryAttack.Clone(); val2.m_name = tier.DisplayName + " spear"; val2.m_damages = new DamageTypes { m_pierce = tier.Damage * 0.75f }; val2.m_backstabBonus = 1f; val2.m_attackForce = 20f; val2.m_useDurability = false; val2.m_aiAttackRangeMin = 1.81f; val2.m_aiAttackRange = 30f; val2.m_aiAttackInterval = 4f; val2.m_aiAttackMaxAngle = 10f; val2.m_ammoType = ""; Attack attack = val2.m_attack; attack.m_attackStamina = (attack.m_attackEitr = (attack.m_attackHealth = 0f)); attack.m_attackProjectile = PrefabManager.Instance.GetPrefab("TWIGDefender_spear_projectile"); attack.m_projectileVel = (attack.m_projectileVelMin = 35f); attack.m_projectileAccuracy = (attack.m_projectileAccuracyMin = 0.5f); attack.m_launchAngle = 0f; attack.m_randomVelocity = (attack.m_skillAccuracy = (attack.m_bowDraw = false)); attack.m_hitFriendly = (attack.m_consumeItem = false); attack.m_recoilPushback = 0f; if (!ItemManager.Instance.AddItem(new CustomItem(val, false))) { throw new InvalidOperationException("Failed to register " + tier.RangedAttackName); } return val; } internal static bool IsSpear(Attack attack) { if (Object.op_Implicit((Object)(object)attack.m_attackProjectile)) { return ((Object)attack.m_attackProjectile).name == "TWIGDefender_spear_projectile"; } return false; } internal static Vector3 Aim(Vector3 from, Character target, float speed, float gravity) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) Vector3 centerPoint = target.GetCenterPoint(); centerPoint += target.GetVelocity() * Mathf.Min(Vector3.Distance(from, centerPoint) / speed, 1f); Vector3 val = centerPoint - from; Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(val.x, 0f, val.z); float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude < 0.01f || gravity <= 0f) { return ((Vector3)(ref val)).normalized; } float num = speed * speed; float num2 = num * num - gravity * (gravity * magnitude * magnitude + 2f * val.y * num); if (num2 < 0f) { return ((Vector3)(ref val)).normalized; } float num3 = (num - Mathf.Sqrt(num2)) / (gravity * magnitude); Vector3 val3 = ((Vector3)(ref val2)).normalized + Vector3.up * num3; return ((Vector3)(ref val3)).normalized; } } [HarmonyPatch(typeof(Attack), "GetProjectileSpawnPoint")] internal static class DefenderSpearAimPatch { private static void Postfix(Attack __instance, Humanoid ___m_character, Vector3 spawnPoint, ref Vector3 aimDir) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (DefenderRangedCombat.IsSpear(__instance) && ((Character)___m_character).GetBaseAI() is TwigDefenderAI twigDefenderAI) { Character targetCreature = ((BaseAI)twigDefenderAI).GetTargetCreature(); if (Object.op_Implicit((Object)(object)targetCreature)) { aimDir = DefenderRangedCombat.Aim(spawnPoint, targetCreature, __instance.m_projectileVel, __instance.m_attackProjectile.GetComponent<Projectile>().m_gravity); } } } } [HarmonyPatch(typeof(Attack), "FireProjectileBurst")] internal static class DefenderSpearReleasePatch { private static bool Prefix(Attack __instance, Humanoid ___m_character) { if (DefenderRangedCombat.IsSpear(__instance)) { if (((Character)___m_character).GetBaseAI() is TwigDefenderAI twigDefenderAI) { return twigDefenderAI.CanFireAtTarget(); } return false; } return true; } } [HarmonyPatch(typeof(Projectile), "OnHit")] internal static class DefenderOrphanedSpearPatch { private static bool Prefix(Projectile __instance, Character ___m_owner) { if (!((Object)__instance).name.StartsWith("TWIGDefender_spear_projectile", StringComparison.Ordinal)) { return true; } if (Object.op_Implicit((Object)(object)___m_owner) && !___m_owner.IsDead() && ___m_owner.GetHealth() > 0f) { return true; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner()) { ZNetScene.instance.Destroy(((Component)__instance).gameObject); } return false; } } [HarmonyPatch(typeof(Piece), "DropResources")] internal static class DefenderRefundOncePatch { private static readonly int RefundedKey = StringExtensionMethods.GetStableHashCode("twig.defenders.refunded"); private static bool Prefix(Piece __instance) { if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<TwigDefenderAI>())) { return true; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner()) { return false; } ZDO zDO = component.GetZDO(); if (zDO.GetBool(RefundedKey, false)) { return false; } zDO.Set(RefundedKey, true); return true; } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class DefenderDeathRefundPatch { private static void Prefix(Character __instance, HitData ___m_lastHit) { if (__instance.GetBaseAI() is TwigDefenderAI) { ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner()) { ((Component)__instance).GetComponent<Piece>().DropResources(___m_lastHit); } } } } [HarmonyPatch(typeof(Player), "Repair")] internal static class DefenderRepairPatch { private static readonly Func<Player, Piece, bool> CheckStation = AccessTools.MethodDelegate<Func<Player, Piece, bool>>(AccessTools.Method(typeof(Player), "CheckCanRemovePiece", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<Player, float> BuildStamina = AccessTools.MethodDelegate<Func<Player, float>>(AccessTools.Method(typeof(Player), "GetBuildStamina", (Type[])null, (Type[])null), (object)null, true); private static bool Prefix(Player __instance, ItemData toolItem) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) Piece hoveringPiece = __instance.GetHoveringPiece(); if (!Object.op_Implicit((Object)(object)hoveringPiece)) { return true; } TwigDefenderAI component = ((Component)hoveringPiece).GetComponent<TwigDefenderAI>(); if (!Object.op_Implicit((Object)(object)component)) { return true; } Character component2 = ((Component)hoveringPiece).GetComponent<Character>(); ZNetView component3 = ((Component)hoveringPiece).GetComponent<ZNetView>(); if (((Character)__instance).IsDead() || !Object.op_Implicit((Object)(object)component3) || !component3.IsValid() || component2.IsDead() || component2.GetHealth() <= 0f) { return false; } Vector3 eyePoint = ((Character)__instance).GetEyePoint(); if (Vector3.Distance(eyePoint, ((Collider)component2.GetCollider()).ClosestPoint(eyePoint)) >= __instance.m_maxPlaceDistance || !CheckStation(__instance, hoveringPiece) || !PrivateArea.CheckAccess(((Component)hoveringPiece).transform.position, 0f, true, false)) { return false; } if (!component.Repair()) { ((Character)__instance).Message((MessageType)1, hoveringPiece.m_name + " $msg_doesnotneedrepair", 0, (Sprite)null, false); return false; } __instance.FaceLookDirection(); ((Component)__instance).GetComponent<ZSyncAnimation>().SetTrigger(toolItem.m_shared.m_attack.m_attackAnimation); hoveringPiece.m_placeEffect.Create(((Component)hoveringPiece).transform.position, ((Component)hoveringPiece).transform.rotation, (Transform)null, 1f, -1, ((Character)__instance).GetZDOID()); ((Character)__instance).Message((MessageType)1, Localization.instance.Localize("$msg_repaired", new string[1] { hoveringPiece.m_name }), 0, (Sprite)null, false); ((Character)__instance).UseStamina(BuildStamina(__instance)); ((Character)__instance).UseEitr(toolItem.m_shared.m_attack.m_attackEitr); if (toolItem.m_shared.m_useDurability) { toolItem.m_durability -= toolItem.m_shared.m_useDurabilityDrain * Game.m_durabilityRate; } return false; } } [HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[] { typeof(Character), typeof(Character) })] internal static class DefenderHostilityPatch { private static void Postfix(Character a, Character b, ref bool __result) { if (__result && Object.op_Implicit((Object)(object)a) && a.GetBaseAI() is TwigDefenderAI) { __result = TargetPolicy.Allows(b); } } } [HarmonyPatch(typeof(WearNTear), "Damage")] internal static class DefenderStructureDamagePatch { private static bool Prefix(HitData hit) { Character attacker = hit.GetAttacker(); if (Object.op_Implicit((Object)(object)attacker)) { return !(attacker.GetBaseAI() is TwigDefenderAI); } return true; } } internal readonly struct TierMaterial { internal string Prefab { get; } internal int Amount { get; } internal TierMaterial(string prefab, int amount) { Prefab = prefab; Amount = amount; } } internal sealed class DefenderTier { internal string PrefabName { get; } internal string AttackName { get; } internal string RangedAttackName { get; } internal string MaterialName { get; } internal string Biome { get; } internal float Health { get; } internal float Damage { get; } internal string Station { get; } internal IReadOnlyList<TierMaterial> Materials { get; } internal string DisplayName => "T.W.I.G. Defender (" + MaterialName + ")"; internal DefenderTier(string suffix, string materialName, string biome, float health, float damage, string station, params TierMaterial[] materials) { PrefabName = "piece_TWIG_Defender" + suffix; AttackName = "TWIGDefender_attack" + suffix; RangedAttackName = "TWIGDefender_throw" + suffix; MaterialName = materialName; Biome = biome; Health = health; Damage = damage; Station = station; Materials = Array.AsReadOnly((TierMaterial[])materials.Clone()); } } internal static class DefenderTiers { internal static IReadOnlyList<DefenderTier> All { get; } = Array.AsReadOnly(new DefenderTier[8] { new DefenderTier("", "Wood", "Meadows", 300f, 20f, "piece_workbench", new TierMaterial("Wood", 10)), new DefenderTier("_Bronze", "Bronze", "Black Forest", 600f, 40f, "forge", new TierMaterial("RoundLog", 20), new TierMaterial("Bronze", 10), new TierMaterial("DeerHide", 4)), new DefenderTier("_Iron", "Iron", "Swamp", 1000f, 65f, "forge", new TierMaterial("ElderBark", 20), new TierMaterial("Iron", 15), new TierMaterial("Chain", 2)), new DefenderTier("_Silver", "Silver", "Mountains", 1400f, 90f, "forge", new TierMaterial("FineWood", 20), new TierMaterial("Silver", 15), new TierMaterial("WolfPelt", 6)), new DefenderTier("_BlackMetal", "Black Metal", "Plains", 1900f, 120f, "forge", new TierMaterial("FineWood", 20), new TierMaterial("BlackMetal", 20), new TierMaterial("LinenThread", 10)), new DefenderTier("_Carapace", "Carapace", "Mistlands", 2500f, 160f, "blackforge", new TierMaterial("YggdrasilWood", 20), new TierMaterial("Carapace", 20), new TierMaterial("Eitr", 5)), new DefenderTier("_Flametal", "Flametal", "Ashlands", 3200f, 210f, "blackforge", new TierMaterial("Blackwood", 20), new TierMaterial("FlametalNew", 20), new TierMaterial("AskHide", 10)), new DefenderTier("_Bloodgold", "Bloodgold", "Deep North", 4000f, 260f, "blackforge", new TierMaterial("Frostwood", 20), new TierMaterial("Gold", 20), new TierMaterial("BjornHide", 10)) }); } internal static class PrefabResearch { internal static void WriteReport() { //IL_01d0: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Valheim: " + Version.GetVersionString(false)); string[] array = new string[10] { "piece_TrainingDummy", "TrainingDummy_attack", "TrainingDummy_attack2", "TrainingDummy_attack3", "TrainingDummy_throw", "TrainingDummy_throw_projectile", "Goblin", "GoblinSpear", "GoblinSpear_projectile", "Bow" }; foreach (string text in array) { GameObject prefab = PrefabManager.Instance.GetPrefab(text); stringBuilder.AppendLine("\nPREFAB " + text + " found=" + ((Object)(object)prefab != (Object)null)); if (!Object.op_Implicit((Object)(object)prefab)) { continue; } Component[] componentsInChildren = prefab.GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren) { if (Object.op_Implicit((Object)(object)val)) { stringBuilder.AppendLine(" " + ((Object)val.transform).name + " :: " + ((object)val).GetType().FullName); if (val is Humanoid || val is BaseAI || val is ZNetView || val is ZSyncTransform || val is ZSyncAnimation || val is Piece || val is Rigidbody || val is Projectile) { Fields(stringBuilder, val, " "); } } } ItemDrop component = prefab.GetComponent<ItemDrop>(); if (Object.op_Implicit((Object)(object)component)) { Fields(stringBuilder, component.m_itemData.m_shared, " shared."); Fields(stringBuilder, component.m_itemData.m_shared.m_attack, " attack."); Fields(stringBuilder, component.m_itemData.m_shared.m_damages, " damage."); } Animator componentInChildren = prefab.GetComponentInChildren<Animator>(true); if (!Object.op_Implicit((Object)(object)componentInChildren) || !Object.op_Implicit((Object)(object)componentInChildren.runtimeAnimatorController)) { continue; } stringBuilder.AppendLine(" Controller: " + ((Object)componentInChildren.runtimeAnimatorController).name); AnimationClip[] animationClips = componentInChildren.runtimeAnimatorController.animationClips; foreach (AnimationClip val2 in animationClips) { stringBuilder.AppendLine(" Clip: " + ((Object)val2).name + " length=" + val2.length); AnimationEvent[] events = val2.events; foreach (AnimationEvent val3 in events) { stringBuilder.AppendLine(" Event: " + val3.functionName + " @ " + val3.time); } } } stringBuilder.AppendLine("\nPROGRESSION MATERIALS"); array = new string[22] { "Wood", "LeatherScraps", "RoundLog", "Bronze", "DeerHide", "ElderBark", "Iron", "Chain", "FineWood", "Silver", "WolfPelt", "BlackMetal", "LinenThread", "YggdrasilWood", "Carapace", "Eitr", "Blackwood", "FlametalNew", "AskHide", "Frostwood", "Gold", "BjornHide" }; foreach (string text2 in array) { GameObject prefab2 = PrefabManager.Instance.GetPrefab(text2); ItemDrop val4 = (Object.op_Implicit((Object)(object)prefab2) ? prefab2.GetComponent<ItemDrop>() : null); stringBuilder.AppendLine(text2 + " => " + (Object.op_Implicit((Object)(object)val4) ? Localization.instance.Localize(val4.m_itemData.m_shared.m_name + " | " + val4.m_itemData.m_shared.m_description) : "MISSING")); } stringBuilder.AppendLine("\nPROGRESSION STATIONS"); array = new string[3] { "piece_workbench", "forge", "blackforge" }; foreach (string text3 in array) { GameObject prefab3 = PrefabManager.Instance.GetPrefab(text3); CraftingStation val5 = (Object.op_Implicit((Object)(object)prefab3) ? prefab3.GetComponent<CraftingStation>() : null); stringBuilder.AppendLine(text3 + " => " + (Object.op_Implicit((Object)(object)val5) ? Localization.instance.Localize(val5.m_name) : "MISSING")); } stringBuilder.AppendLine("\nDEEP NORTH RECIPE CROSS-CHECK"); Recipe[] array2 = Resources.FindObjectsOfTypeAll<Recipe>(); foreach (Recipe val6 in array2) { if (!Object.op_Implicit((Object)(object)val6.m_item) || !((Object)val6.m_item).name.StartsWith("ArmorDeepNorth", StringComparison.Ordinal)) { continue; } stringBuilder.Append(((Object)val6.m_item).name + " station=" + (Object.op_Implicit((Object)(object)val6.m_craftingStation) ? ((Object)val6.m_craftingStation).name : "none") + " materials="); Requirement[] resources = val6.m_resources; foreach (Requirement val7 in resources) { if (Object.op_Implicit((Object)(object)val7.m_resItem)) { stringBuilder.Append(((Object)val7.m_resItem).name + " x" + val7.m_amount + "; "); } } stringBuilder.AppendLine(); } string text4 = Path.Combine(Paths.ConfigPath, "TWIG-prefab-research.txt"); File.WriteAllText(text4, stringBuilder.ToString()); TwigDefenderPlugin.Log("Research written to " + text4); } private static void Fields(StringBuilder output, object value, string prefix) { FieldInfo[] fields = value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { object value2 = fieldInfo.GetValue(value); Object val = (Object)((value2 is Object) ? value2 : null); if (val != null) { output.AppendLine(prefix + fieldInfo.Name + "=" + val.name); } else if (value2 is IEnumerable enumerable && !(value2 is string)) { output.Append(prefix + fieldInfo.Name + "=["); foreach (object item in enumerable) { Object val2 = (Object)((item is Object) ? item : null); output.Append(((val2 != null) ? val2.name : item)?.ToString() + ", "); } output.AppendLine("]"); } else if (value2 == null || value2 is string || value2.GetType().IsValueType) { output.AppendLine(prefix + fieldInfo.Name + "=" + value2); } } } } internal static class TargetPolicy { internal static bool Allows(Character candidate) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected I4, but got Unknown if (!Object.op_Implicit((Object)(object)candidate) || candidate.IsDead() || candidate.IsPlayer() || candidate.IsTamed() || candidate.m_aiSkipTarget || candidate.GetBaseAI() is TwigDefenderAI || candidate.GetBaseAI() is AnimalAI) { return false; } Faction faction = candidate.GetFaction(); switch (faction - 2) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 11: return true; case 8: if (Object.op_Implicit((Object)(object)candidate.GetBaseAI())) { return candidate.GetBaseAI().IsAggravated(); } return false; default: return false; } } } public sealed class TwigDefenderAI : BaseAI { private const float DetectionRange = 30f; private const float ScanInterval = 0.5f; private const float LostSightTimeout = 2f; private static readonly int AnchorKey = StringExtensionMethods.GetStableHashCode("twig.defenders.anchor"); private static readonly int AnchorSetKey = StringExtensionMethods.GetStableHashCode("twig.defenders.anchorSet"); private readonly List<Character> candidates = new List<Character>(); private Humanoid humanoid; private Character target; private Vector3 anchor; private float nextScan; private float nextAttack; private float lastSeen; private bool wasOwner; private float lastRepair = float.NegativeInfinity; protected override void Awake() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) ((BaseAI)this).Awake(); humanoid = ((Component)this).GetComponent<Humanoid>(); ((Character)humanoid).SetLookDir(((Component)this).transform.forward, 0f); base.m_viewRange = 30f; base.m_viewAngle = 360f; base.m_hearRange = 0f; nextScan = Time.time + Random.Range(0f, 0.5f); TwigDefenderPlugin.DebugLog("Defender initialized; owner=" + base.m_nview.IsOwner()); } public override Character GetTargetCreature() { return target; } internal bool CanFireAtTarget() { if (base.m_nview.IsValid() && base.m_nview.IsOwner() && !((Character)humanoid).IsDead() && ((Character)humanoid).GetHealth() > 0f && IsValidTarget(target)) { return ((BaseAI)this).CanSeeTarget(target); } return false; } internal bool Repair() { if (!base.m_nview.IsValid() || ((Character)humanoid).IsDead() || ((Character)humanoid).GetHealth() <= 0f || ((Character)humanoid).GetHealth() >= ((Character)humanoid).GetMaxHealth() || Time.time - lastRepair < 1f) { return false; } lastRepair = Time.time; ((Character)humanoid).Heal(((Character)humanoid).GetMaxHealth(), false); return true; } public override bool UpdateAI(float dt) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) bool flag = base.m_nview.IsValid() && base.m_nview.IsOwner(); if (flag != wasOwner) { wasOwner = flag; target = null; nextAttack = Time.time + 2f; TwigDefenderPlugin.DebugLog("Ownership changed; owner=" + flag); if (flag) { ((Character)humanoid).SetLookDir(((Component)this).transform.forward, 0f); ZDO zDO = base.m_nview.GetZDO(); if (!zDO.GetBool(AnchorSetKey, false)) { zDO.Set(AnchorKey, ((Component)this).transform.position); zDO.Set(AnchorSetKey, true); } anchor = zDO.GetVec3(AnchorKey, ((Component)this).transform.position); ((BaseAI)this).SetTargetInfo(ZDOID.None); } } if (!flag || ((Character)humanoid).IsDead()) { return false; } ((BaseAI)this).StopMoving(); Vector3 val = ((Component)this).transform.position - anchor; if (((Vector3)(ref val)).sqrMagnitude > 0.0001f) { ((Component)this).transform.position = anchor; base.m_body.position = anchor; } if (Object.op_Implicit((Object)(object)target) && !IsValidTarget(target)) { ClearTarget("dead, friendly, or out of range"); } if (Time.time >= nextScan) { nextScan = Time.time + 0.5f; if (Object.op_Implicit((Object)(object)target)) { if (((BaseAI)this).CanSeeTarget(target)) { lastSeen = Time.time; } else if (Time.time - lastSeen >= 2f) { ClearTarget("line of sight lost"); } } if (!Object.op_Implicit((Object)(object)target)) { TargetScan(); } } if (!Object.op_Implicit((Object)(object)target)) { return true; } ((BaseAI)this).LookAt(target.GetCenterPoint()); RequestAttack(); return true; } private bool IsValidTarget(Character candidate) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (TargetPolicy.Allows(candidate) && BaseAI.IsEnemy((Character)(object)humanoid, candidate)) { Vector3 val = ((Component)candidate).transform.position - ((Component)this).transform.position; return ((Vector3)(ref val)).sqrMagnitude <= 900f; } return false; } private void TargetScan() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) candidates.Clear(); Character.GetCharactersInRange(((Component)this).transform.position, 30f, candidates); float num = float.MaxValue; foreach (Character candidate in candidates) { if (IsValidTarget(candidate)) { Vector3 val = ((Component)candidate).transform.position - ((Component)this).transform.position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude >= num) && ((BaseAI)this).CanSeeTarget(candidate)) { target = candidate; num = sqrMagnitude; } } } if (Object.op_Implicit((Object)(object)target)) { lastSeen = Time.time; ((BaseAI)this).SetAlerted(true); ((BaseAI)this).SetTargetInfo(target.GetZDOID()); TwigDefenderPlugin.DebugLog("Target acquired: " + ((Object)target).name); } } private void ClearTarget(string reason) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) target = null; ((BaseAI)this).SetTargetInfo(ZDOID.None); ((BaseAI)this).SetAlerted(false); TwigDefenderPlugin.DebugLog("Target invalidated: " + reason); } private void RequestAttack() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) if (Time.time < nextAttack || ((Character)humanoid).InAttack()) { return; } humanoid.EquipBestWeapon(target, (StaticTarget)null, (Character)null, (Character)null); ItemData currentWeapon = humanoid.GetCurrentWeapon(); if (currentWeapon != null && ((BaseAI)this).CanUseAttack(currentWeapon)) { float num = Vector3.Distance(((Component)this).transform.position, ((Component)target).transform.position) - target.GetRadius(); SharedData shared = currentWeapon.m_shared; if (!(num < shared.m_aiAttackRangeMin) && !(num > shared.m_aiAttackRange) && ((BaseAI)this).IsLookingAt(((Component)target).transform.position, shared.m_aiAttackMaxAngle, false) && ((BaseAI)this).CanSeeTarget(target) && ((Character)humanoid).StartAttack(target, false)) { nextAttack = Time.time + Mathf.Max(2f, shared.m_aiAttackInterval); TwigDefenderPlugin.DebugLog("Native attack requested: " + ((Object)target).name); } } } } [BepInPlugin("twig.defenders", "T.W.I.G. Defenders", "0.4.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class TwigDefenderPlugin : BaseUnityPlugin { public const string PluginGuid = "twig.defenders"; public const string PluginVersion = "0.4.0"; private Harmony harmony; private ConfigEntry<bool> debugLogging; internal static TwigDefenderPlugin Instance { get; private set; } private void Awake() { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown Instance = this; string[] commandLineArgs = Environment.GetCommandLineArgs(); if (Array.IndexOf(commandLineArgs, "-twig-sandbox") >= 0) { int num = Array.IndexOf(commandLineArgs, "-savedir"); if (num < 0 || num + 1 >= commandLineArgs.Length) { throw new ArgumentException("-twig-sandbox requires -savedir."); } string fullPath = Path.GetFullPath(commandLineArgs[num + 1]); string fullPath2 = Path.GetFullPath(Paths.GameRootPath); char directorySeparatorChar = Path.DirectorySeparatorChar; if (!fullPath.StartsWith(fullPath2 + directorySeparatorChar, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Sandbox saves must be inside the sandbox game directory."); } Utils.SetSaveDataPath(fullPath); } debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "DebugLogging", false, "Log target changes, ownership changes, and attack requests."); harmony = new Harmony("twig.defenders"); harmony.PatchAll(typeof(TwigDefenderPlugin).Assembly); PrefabManager.OnVanillaPrefabsAvailable += Register; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[TWIG] Plugin loaded; waiting for vanilla prefabs."); } private void Register() { PrefabManager.OnVanillaPrefabsAvailable -= Register; int num = 0; try { PrefabResearch.WriteReport(); DefenderPrefab.Register(); } catch (Exception ex) { num = 1; ((BaseUnityPlugin)this).Logger.LogError((object)ex); } if (Array.IndexOf(Environment.GetCommandLineArgs(), "-twig-inspect") >= 0) { Application.Quit(num); } } internal static void Log(string message) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[TWIG] " + message)); } internal static void DebugLog(string message) { if (Instance.debugLogging.Value) { Log(message); } } private void OnDestroy() { PrefabManager.OnVanillaPrefabsAvailable -= Register; Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } } }