Decompiled source of TWIG Defenders v0.4.0

BepInEx/plugins/TwigDefenders/TwigDefenders.dll

Decompiled 3 weeks ago
using 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();
			}
		}
	}
}