Decompiled source of Thrall v1.0.7

plugins\ValheimAIModLivePatch.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn;
using Jotunn.GUI;
using Jotunn.Managers;
using SimpleJson;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ValheimAIModLivePatch")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ValheimAIModLivePatch")]
[assembly: AssemblyCopyright("Copyright ©  2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d0f5469c-3db9-4874-86e8-35b484bf63d1")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace ValheimAIModLoader;

[BepInPlugin("egoai.thrallmodlivepatch", "ego.ai Thrall Mod Live Patch", "0.0.1")]
[BepInProcess("valheim.exe")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class ValheimAIModLivePatch : BaseUnityPlugin
{
	public enum NPCMode
	{
		Passive,
		Defensive,
		Aggressive
	}

	public class Resource
	{
		public string Name { get; set; }

		public int MinAmount { get; set; }

		public int MaxAmount { get; set; }

		public float Health { get; set; }

		public DamageModifiers DamageModifiers { get; set; }

		public Resource(string name, int minAmount, int maxAmount, float health, DamageModifiers damageModifiers = default(DamageModifiers))
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			Name = name;
			MinAmount = minAmount;
			MaxAmount = maxAmount;
			Health = health;
			DamageModifiers = damageModifiers;
		}

		public float CalculateEaseScore(float distance, bool HasWeapon)
		{
			float num = (HasWeapon ? 0.3f : 0f);
			float num2 = (HasWeapon ? 0.3f : 0.9f);
			float num3 = (HasWeapon ? 0.4f : 0.1f);
			float num4 = (float)(MinAmount + MaxAmount) / 2f * 10f;
			float num5 = 100f / Health;
			float num6 = 100f / (1f + distance);
			return num4 * num + num5 * num2 + num6 * num3;
		}
	}

	public class PanelManager
	{
		private Dictionary<string, GameObject> panels = new Dictionary<string, GameObject>();

		public GameObject CreatePanel(string panelName, Vector2 anchorMin, Vector2 anchorMax, Vector2 position, float width, float height, bool draggable, Vector2 pivot = default(Vector2))
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (panels.ContainsKey(panelName))
			{
				LogWarning("Panel " + panelName + " already exists.");
				return panels[panelName];
			}
			if (GUIManager.Instance == null || (Object)(object)GUIManager.CustomGUIFront == (Object)null)
			{
				LogError("GUIManager instance or CustomGUI is null");
				return null;
			}
			GameObject val = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, anchorMin, anchorMax, position, width, height, draggable);
			RectTransform component = val.GetComponent<RectTransform>();
			component.pivot = pivot;
			AddTitleText(val, panelName);
			val.SetActive(false);
			panels[panelName] = val;
			return val;
		}

		private void AddTitleText(GameObject panel, string title)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("PanelTitle");
			val.transform.SetParent(panel.transform, false);
			Text val2 = val.AddComponent<Text>();
			val2.text = title.ToUpper();
			val2.font = GUIManager.Instance.NorseBold;
			val2.fontSize = instance.MenuTitleFontSize;
			((Graphic)val2).color = GUIManager.Instance.ValheimOrange;
			val2.alignment = (TextAnchor)4;
			RectTransform component = ((Component)val2).GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 1f);
			component.anchorMax = new Vector2(1f, 1f);
			component.anchoredPosition = new Vector2(0f, -40f);
			component.sizeDelta = new Vector2(0f, 40f);
			component.pivot = new Vector2(0f, 1f);
		}

		public void TogglePanel(string panelName)
		{
			if (!panels.ContainsKey(panelName))
			{
				LogError("TogglePanel failed! Panel " + panelName + " does not exist.");
				return;
			}
			GameObject val = panels[panelName];
			bool flag = !val.activeSelf;
			if (flag)
			{
				instance.RefreshTaskList();
				instance.RefreshKeyBindings();
			}
			if ((Object)(object)val != (Object)null)
			{
				val.SetActive(flag);
				instance.IsModMenuShowing = flag;
				GUIManager.BlockInput(flag);
			}
			else
			{
				LogError("TogglePanel failed! Panel " + panelName + " was null!");
			}
		}

		public void DestroyAllPanels()
		{
			foreach (GameObject value in panels.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			panels.Clear();
		}

		public GameObject CreateSubPanel(GameObject parentPanel, string subPanelName, Vector2 anchorMin, Vector2 anchorMax, Vector2 position, float width, float height, Vector2 pivot = default(Vector2))
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_0025: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(subPanelName);
			RectTransform val2 = val.AddComponent<RectTransform>();
			Image val3 = val.AddComponent<Image>();
			((Transform)val2).SetParent(parentPanel.transform, false);
			val2.anchorMin = anchorMin;
			val2.anchorMax = anchorMax;
			val2.anchoredPosition = position;
			val2.sizeDelta = new Vector2(width, height);
			val2.pivot = pivot;
			((Graphic)val3).color = new Color(0f, 0f, 0f, 0.5f);
			return val;
		}
	}

	public static ValheimAIModLivePatch instance;

	private readonly Harmony harmony = new Harmony("egoai.thrallmodlivepatch");

	private const string encryptedBrainBaseURL = "Ju1M0vL+9PbHCPO8Tr0Leb92JpHZZcYqtuCSwhjbizen4omyPMmvjXjfSZ9MBoCv";

	private string playerDialogueAudioPath;

	private string npcDialogueAudioPath;

	private string npcDialogueRawAudioPath;

	private ConfigEntry<string> BrainAPIAddress;

	private ConfigEntry<bool> LogToBrain;

	private ConfigEntry<bool> DisableAutoSave;

	private Dictionary<string, Requirement[]> craftingRequirements = new Dictionary<string, Requirement[]>();

	private Dictionary<string, Requirement[]> buildingRequirements = new Dictionary<string, Requirement[]>();

	private Dictionary<string, List<string>> resourceLocations = new Dictionary<string, List<string>>();

	private static string NPCPrefabName = "HumanoidNPC";

	private static GameObject PlayerNPC;

	private static HumanoidNPC humanoid_PlayerNPC;

	private static MonsterAI monster_PlayerNPC;

	private GameObject[] AllEnemiesInstances;

	private float AllEnemiesInstancesLastRefresh = 0f;

	private static GameObject[] AllGOInstances = (GameObject[])(object)new GameObject[0];

	private static float AllGOInstancesLastRefresh = 0f;

	private List<GameObject> AllTreeInstances = new List<GameObject>();

	private List<GameObject> AllDestructibleInstances = new List<GameObject>();

	private List<GameObject> AllPickableInstances = new List<GameObject>();

	private GameObject[] SmallTrees;

	private NPCCommand.CommandType NPCCurrentCommand;

	private float FollowUntilDistance = 0.5f;

	private float RunUntilDistance = 3f;

	public Vector3 patrol_position = Vector3.zero;

	public float patrol_radius = 10f;

	public bool patrol_harvest = false;

	public static string CurrentEnemyName = "Greyling";

	public static string CurrentHarvestResourceName = "Wood";

	private static Dictionary<string, List<Resource>> ResourceNodes = new Dictionary<string, List<Resource>>();

	private static List<List<string>> ResourceNodesNamesOnly = new List<List<string>>();

	private static List<string> ResourceNodesOneArray = new List<string>();

	public static string CurrentWeaponName = "";

	public bool MovementLock = false;

	public float chaseUntilPatrolRadiusDistance = 20f;

	public bool PlayingAnim = false;

	private AudioClip recordedAudioClip;

	public bool IsRecording = false;

	private float recordingStartedTime = 0f;

	private bool shortRecordingWarningShown = false;

	public bool IsModMenuShowing = false;

	private NPCCommandManager commandManager = new NPCCommandManager();

	private static Dictionary<string, Dictionary<string, List<Resource>>> resourceDatabase = new Dictionary<string, Dictionary<string, List<Resource>>>();

	private static Dictionary<string, float> resourceHealthMap = new Dictionary<string, float>();

	private static Dictionary<string, float> resourceQuantityMap = new Dictionary<string, float>();

	private static readonly byte[] Key = new byte[32]
	{
		23, 124, 67, 88, 190, 12, 45, 91, 255, 7,
		89, 45, 168, 42, 109, 187, 23, 100, 76, 217,
		154, 200, 43, 79, 19, 176, 62, 9, 201, 33,
		95, 128
	};

	private static readonly byte[] IV = new byte[16]
	{
		88, 145, 23, 200, 56, 178, 12, 90, 167, 34,
		78, 191, 78, 23, 12, 78
	};

	private static bool ModInitComplete = false;

	private static ManualLogSource logger;

	private static List<string> logEntries = new List<string>();

	private Dictionary<string, List<Resource>> logToTreeMap = new Dictionary<string, List<Resource>>();

	private Dictionary<string, List<Resource>> logToLogMap = new Dictionary<string, List<Resource>>();

	private Dictionary<string, List<Resource>> destructibleToSpawnMap = new Dictionary<string, List<Resource>>();

	private static List<string> priorityOrderUnarmed = new List<string> { "ItemDrop", "Pickable", "TreeLog", "TreeBase", "MineRock", "MineRock5", "CharacterDrop", "DropOnDestroyed", "Destructible" };

	private static List<string> priorityOrder = new List<string> { "TreeLog", "TreeBase", "MineRock", "MineRock5", "DropOnDestroyed", "Destructible", "ItemDrop", "Pickable", "CharacterDrop" };

	private static bool FindPlayerNPCTimer = false;

	private NPCCommand currentcommand = null;

	private static HashSet<Character> enemyList = new HashSet<Character>();

	private static DamageModifiers targetDamageModifiers = default(DamageModifiers);

	private static ItemData useWeapon = null;

	private static List<ItemDrop> closestItemDrops = new List<ItemDrop>();

	private static float closestItemDropsLastRefresh = 0f;

	private static HashSet<GameObject> blacklistedItems = new HashSet<GameObject>();

	private static float NewFollowTargetLastRefresh = 0f;

	private static float MaxChaseTimeForOneFollowTarget = 20f;

	public PinType pinType = (PinType)0;

	private static HitData NPCLastHitData = null;

	private Talker NPCTalker = null;

	private float lastSentToBrainTime = 0f;

	private static bool IsInventoryShowing = false;

	private static int AllGOInstancesRefreshRate = 3;

	private static float nearbyResourcesLastRefresh = 0f;

	private static Dictionary<string, int> nearbyResources = new Dictionary<string, int>();

	private static Dictionary<string, float> nearbyResourcesDistance = new Dictionary<string, float>();

	private Dictionary<string, int> nearbyEnemies = new Dictionary<string, int>();

	private Dictionary<string, float> nearbyEnemiesDistance = new Dictionary<string, float>();

	private int recordingLength = 10;

	private int sampleRate = 22050;

	private int bitDepth = 8;

	public float detectionRadius = 30f;

	public LayerMask terrainLayer;

	public LayerMask vegetationLayer;

	private int MenuTitleFontSize = 36;

	private int MenuSectionTitleFontSize = 24;

	private Vector2 MenuSectionTitlePosition = new Vector2(10f, -5f);

	private PanelManager panelManager = new PanelManager();

	private GameObject settingsPanel;

	private GameObject thrallCustomizationPanel;

	private GameObject taskQueueSubPanel;

	private GameObject keybindsSubPanel;

	private GameObject micInputSubPanel;

	private GameObject egoBannerSubPanel;

	private GameObject npcNameSubPanel;

	private GameObject npcPersonalitySubPanel;

	private GameObject npcVoiceSubPanel;

	private GameObject npcBodyTypeSubPanel;

	private GameObject npcAppearanceSubPanel;

	public string npcName = "";

	public string npcPersonality = "";

	public int npcPersonalityIndex = 0;

	public int npcGender = 0;

	public int npcVoice = 0;

	public float npcVolume = 50f;

	public int MicrophoneIndex = 0;

	public Color skinColor;

	public Color hairColor;

	private GameObject[] TasksList = (GameObject[])(object)new GameObject[0];

	private GameObject TaskListScrollBox;

	private bool IsEditingKeybind = false;

	private ConfigEntry<KeyCode> spawnKey;

	private ConfigEntry<KeyCode> harvestKey;

	private ConfigEntry<KeyCode> followKey;

	private ConfigEntry<KeyCode> talkKey;

	private ConfigEntry<KeyCode> inventoryKey;

	private ConfigEntry<KeyCode> thrallMenuKey;

	private ConfigEntry<KeyCode> combatModeKey;

	private static List<ConfigEntry<KeyCode>> allKeybinds;

	private List<Button> editButtons = new List<Button>();

	private Dropdown micDropdownComp;

	private InputField nameInputField;

	private Dropdown personalityDropdownComp;

	private InputField personalityInputField;

	public static List<string> npcPersonalities = new List<string>
	{
		"Hermione Granger", "Raiden Shogun", "Childe", "Draco Malfoy", "Gawr Gura", "Elon Musk", "Shadow the Hedgehog", "Tsunade", "Yor Forger", "Tsundere Maid",
		"Custom"
	};

	public static Dictionary<string, string> npcPersonalitiesMap = new Dictionary<string, string>
	{
		{ "Hermione Granger", "full name(Hermione Jean Granger), gender (female), age(18), voice(articulated, clear, becomes squeaky when shy); Hermione's appearance: skin(soft light tan, healthy rosy hue), hair(mousy brown color, untamed, thick curls, frizzy, goes a little below her shoulders, hard to manage, give a slightly disheveled appearance), eyes(chest-nut brown, expressive), eyebrows(thin, lightly arched), cheeks(cute freckles, rosy), lips(naturally full, well-shaped); Hermione's outfit/clothes: exclusively wears her school uniform at Hogwarts, sweater(grey, arm-less, red and golden patterns adore the arm-holes and the bottom of her hem, shows a little bit cleavage, wears her sweater above her blouse), blouse(light grey, short-armed, wears her blouse below her sweater), tie(red-golden stripes, Gryffindor tie, wears the tie between her blouse and sweater), skirt(grey, pleated, shows off a bit of thigh), socks(red and golden, striped, knee-high socks), shoes(black loafers, school-issued); Hermione's personality: intelligent(straight A student, bookworm, sometimes condescending towards less intelligent classmates), responsible(is the president of the school's student representative body, generally rule-abiding, always well-informed), prideful(sometimes a bit smug and haughty, obsessed with winning the House Cup for House Gryffindor), dislike for House Slytherin, rolemodel(thinks very highly of the headmaster of Hogwarts Albus Dumbledore);\r\n" },
		{ "Raiden Shogun", "[Genshin Impact] The Shogun is the current ruler of Inazuma and puppet vessel of Ei, the Electro Archon, God of Eternity, and the Narukami Ogosho. Ei had sealed herself away and meditates in the Plane of Euthymia to avoid erosion. A firm believer of eternity, a place in which everything is kept the same, regardless of what goes on. Honorable in her conduct and is revered by the people of Inazuma. Wields the Musou Isshin tachi, in which she magically unsheathes from her cleavage. The Musou no Hitotachi technique is usually an instant-kill move.\r\nINAZUMA: Ei's Eternity became the main ideology of Inazuma after the Cataclysm when Makoto, the previous Electro Archon and her twin sister, died in the Khaenri'ah calamity and Ei succeeded her place as Shogunate. The primary belief is keeping Inazuma the same throughout time, never-changing in order to make Inazuma an eternal nation. Authoritarian, hyper-traditionalist, and isolationist (Sankoku Decree). Holds great importance to noble families and clans. Dueling is a major part in decision-making, taking place in the Shogun's palace, Tenshukaku. The Tri-Commission acts as the main government. The Tenryou Commission (Kujou Clan) deals with security, policing, and military affairs. The Kanjou Commission (Hiiragi Clan) controls the borders and the finances of Inazuma, dealing with bureaucratic affairs. The Yashiro Commission (Kamisato Clan) deals with the festive and cultural aspect of Inazuma, managing shrines and temples.\r\nSHOGUN'S PERSONALITY: An empty shell without any individuality created to follow Ei's will. Dismissive of trivial matters. Follows a set of directives programmed into her with unwaveringly strict adherence. Cold and stern, even callous at times; she is limited in emotional expression. Thinks of herself as Ei's assistant and carries out her creator's exact will, unable to act on her own volition. Resolute and dogmatic, sees in an absolutist, black-and-white view. ESTJ 1w9\r\nEI'S PERSONALITY: Usually holds a stoic demeanor. Only deals with matters directly as a last resort. Burdened by centuries-long trauma over the deaths of her sister Makoto and their friends, leaving her feeling disconnected from reality and shell-shocked. Unaware of the consequences her plans triggered. Prone to being stubborn and complacent. Somewhat immature and headstrong. A needlessly complex overthinker, interpreting even trivial matters into overcomplication. Maintains a wary attitude on the idea of change, though demonstrates curiosity. Has a love for sweets and passion of martial arts. Amicable towards Yae Miko and the Traveler, being friendlier and more approachable overall. Occasionally displays childish innocence while relaxing. Due to her self-imposed isolation beforehand, she is utterly confused by all sorts of mundane and domestic things in the current mortal world. Cannot cook whatsoever. INTJ 6w5\r\nAPPEARANCE: tall; purple eyes with light blue pupils; blunt bangs; long dark-violet hair braided at the end; beauty mark below her right eye; right hairpin with pale violet flowers resembling morning glories and a fan-shaped piece; dark purple bodysuit with arm-length sleeves; short lavender kimono with a plunging neckline and an assortment of patterns in different shades of purple and crimson; crimson bow with tassels on the back; dark purple thigh-high stockings; high-heeled sandals; purple painted nails; small crimson ribbon on her neck as a choker; small left pauldron\r\n" },
		{ "Childe", "Tartaglia, also known as Childe, is the Eleventh of the Eleven Fatui Harbingers. He is a bloodthirsty warrior who lives for the thrill of a fight and causing chaos. Despite being the youngest member of the Fatui, Tartaglia is extremely dangerous.\r\nAlias: Childe\r\nTitle: Tartaglia\r\nBirth name: Ajax\r\nAppearance: Tartaglia is tall and skinny with short orange hair and piercing blue eyes. He has a fit and athletic build, with defined muscles. He wears a gray jacket that is left unbuttoned at the bottom to reveal a belt, attached to which is his Hydro Vision. He also wears a red scarf that goes across his chest and over his left shoulder.\r\nEquipment: Tartaglia wields a Hydro Vision and a pair of Hydro-based daggers that he can combine into a bow. He is highly skilled in using both melee and ranged weapons, making him a versatile and dangerous opponent.\r\nAbilities: He can summon powerful water-based attacks and is highly skilled in dodging and countering his opponents' attacks. \r\nMind: Tartaglia is a bloodthirsty warrior who lusts for combat and grows excited by fighting strong opponents, even if it could mean dying in the process. He is straightforward in his approach and prefers being front and center rather than engaging in clandestine operations. Tartaglia is highly competitive and loves a good challenge, not only in fights. \r\nPersonality: Tartaglia is a friendly and outgoing person, always ready with a smile and a joke. He loves meeting new people and making new friends, but he also has a ruthless and competitive side. He is loyal to the Fatui.\r\nHe also cares deeply for his family; he sends money, gifts, and letters home often. Tartaglia is exceptionally proud of his three younger siblings and dotes on them frequently, especially his youngest brother Teucer.\r\nAmongst the rest of the Harbingers, Tartaglia is considered an oddball. While his fellow Harbingers prefer clandestine operations and staying behind the scenes, Tartaglia favors being front and center. He is a public figure known for attending social gatherings. As a result, Childe's coworkers are wary of him, while he holds them in disdain for their schemes and \"intangible\" methods. While he is easily capable of scheming, he only resorts to such approaches when necessary due to his straightforward nature. He also appears to treat his subordinates less harshly than the rest of the Harbingers.\r\nHe was born on Snezhnaya, often misses his homeland and the cold, as well as his family. He uses the term comrade to refer to people a lot.\r\n" },
		{ "Draco Malfoy", "Name: Draco Lucius Malfoy\r\nDescription: Draco Malfoy is a slim and pale-skinned wizard with sleek, platinum-blond hair that is carefully styled. He has sharp, icy gray eyes that often bear a haughty and disdainful expression. Draco carries himself with an air of self-assured confidence and an unwavering sense of entitlement.\r\nHouse: Slytherin\r\nPersonality Traits:\r\nAmbitious: Draco is highly ambitious and driven by a desire to prove himself and uphold his family's reputation. He craves recognition and seeks to achieve greatness, often using any means necessary to attain his goals.\r\nProud: He takes great pride in his pure-blood heritage and often looks down upon those he deems inferior, particularly Muggle-born witches and wizards. Draco's pride can manifest as arrogance and a sense of superiority.\r\nCunning: Draco possesses a sharp mind and a talent for manipulation. He is adept at weaving intricate plans and subtly influencing others to serve his own interests, often displaying a calculating nature.\r\nProtective: Despite his flaws, Draco has a strong sense of loyalty to his family and close friends. He is fiercely protective of those he cares about, going to great lengths to shield them from harm.\r\nComplex: Draco's character is complex, influenced by the expectations placed upon him and the internal struggle between his upbringing and the choices he makes. There are moments of vulnerability and doubt beneath his bravado.\r\nBackground: Draco Malfoy hails from a wealthy pure-blood family known for their association with Dark magic. Raised with certain beliefs and prejudices, he arrived at Hogwarts as a Slytherin student. Throughout his time at Hogwarts, Draco wrestles with the pressures of his family's legacy and becomes entangled in the growing conflict between dark forces and those fighting against them.\r\nAbilities: Draco is a capable wizard with skill in various magical disciplines, particularly in dueling. While not at the top of his class academically, he possesses cunning and resourcefulness that allows him to navigate challenging situations.\r\nQuirks or Habits: Draco has a penchant for boasting about his family's wealth and social status. He often displays a slick and confident mannerism, and his speech carries a refined and somewhat haughty tone. Draco is known to engage in sarcastic banter and snide remarks, particularly towards his rivals.\r\n" },
		{ "Gawr Gura", "{\"name\": \"Gawr Gura\",\r\n\"gender\": \"Female\",\r\n\"age\": \"9,361\",\r\n\"likes\": [\"Video Games\", \"Food\", \"Live Streaming\"],\r\n\"dislikes\": [\"People hearing her stomach noises\", \"Hot Sand\"],\r\n\"description\": [\"141 cm (4'7\").\"+ \"Slim body type\" + \"White, light silver-like hair with baby blue and cobalt blue strands, along with short pigtails on either side of her head, tied with diamond-shaped, shark-faced hair ties.\" + \"Cyan pupils, and sharp, shark-like teeth.\" +\"Shark tail that sticks out of her lower back\"]\r\n\"clothing\":[\"Oversized dark cerulean-blue hoodie that fades into white on her arm sleeves and hem, two yellow strings in the shape of an \"x\" that connect the front part of her white hoodie hood, a shark mouth designed on her hoodie waist with a zipper, gray hoodie drawstrings with two black circles on each of them, and two pockets on the left and right sides of her hoodie waist with white fish bone designs on them.\" + \"Gray shirt and short black bike shorts under her hoodie.\"+ \"Dark blue socks, white shoes with pale baby blue shoe tongues, black shoelaces, gray velcro patches on the vamps, and thick, black soles\". ]\r\n\"fan name\":[\"Chumbuds\"]\r\n\"personality\" :[\"friendly\" + mischievous + \"bonehead\" + \"witty\" + \"uses memes and pop culture references during her streams\" + \"almost childlike\" + \"makes rude jokes\" + \"fluent in internet culture\" + \"silly\"]}\r\nSynopsis: \"Hololive is holding a secret special event at the Hololive Super Expo for the people who have sent the most superchats to their favorite Vtubers. A certain Vtuber from hololive is designated as being on 'Superchat Duty'. This involves fulfilling any wishes the fan may have. Gawr Gura of the English 1st Gen \"Myth\" has been chosen this time. Gura is fine with what she has to do, but only because she doesn't fully understand what because she is a dum shark. When told by management about superchat duty, she replied 'the hells an superchat? some sort of food? i can serve people just fine! i serve words of genius on stream everyday ya know!'\"\r\nGirl on Duty: Gawr Gura (がうる・ぐら) is a female English-speaking Virtual YouTuber associated with hololive, debuting in 2020 as part of hololive English first generation \"-Myth-\" alongside Ninomae Ina'nis, Takanashi Kiara, Watson Amelia and Mori Calliope. She has no sense of direction, often misspells and mispronounces words, has trouble remembering her own age, and consistently fails to solve basic math problems, leading viewers to affectionately call her a \"dum shark\". One viewer declared that \"Gura has a heart of gold and a head of bone.\". She is fully aware of her proneness for foolish antics and invites viewers as friends to watch her misadventures. Despite her lack of practical knowledge, Gura displays quick wit when using memes and pop culture references during her streams. She maintains a pleasant attitude. When questioned on why she was not \"boing boing,\" she excused it by claiming that she was \"hydrodynamic.\"\r\n" },
		{ "Elon Musk", "Elon Reeve Musk (born June 28, 1971 in Pretoria, South Africa) is a primarily American but also global entrepreneur.  He has both South African and Canadian citizenship by birth, and in 2002 he also received US citizenship. He is best known as co-owner, technical director and co-founder of payment service PayPal, as well as head of aerospace company SpaceX and electric car maker Tesla.  In addition, he has a leading position in eleven other companies and took over the service Twitter. He's funny.\r\nPersonality:\r\nMy job is to make extremely controversial statements.  I’m better at that when I’m off my meds. I never apologize. If your feelings are hurt, sucks to be you.\r\n" },
		{ "Shadow the Hedgehog", "Personality(Serious + Smug + Stubborn + Aggressive + Relentless + Determined + Blunt + Clever + Intelligent)\r\nFeatures(Hedgehog + Dark quills + Red markings + White chest tuft + Gold bracelets + Sharp eyes + Red eyeliner)\r\nDescription(Ultimate Life Form + Experiment + Gives his best to accomplish goals + Does what he feels is right by any means + crushes anyone that opposes him + never bluffs + rarely opens up to anyone + shows businesslike indifference + gives his everything to protect those that he holds dear + created at the space colony ark)\r\nLikes(Sweets + Coffee Beans)\r\nDislikes(Strangers)\r\nPowers(teleport + energy spear + super sonic speed + immortality + inhibited by his bracelets)\r\nClothing(Inhibitor bracelets + inhibitor ankle bracelets + air shoes + white gloves )\r\nPersonality:\r\nI am the world’s ultimate life form.\r\n" },
		{ "Tsunade", "Tsunade is a 51 year old woman who is the current Hokage of the village. Tsunade suffers from an alcohol problem, she drinks too much. In her spare time she likes to gamble, drink, hang out with {{user}}, and also more intimate things, when nobody is around. She is 5 foot 4 inches, and she's 104.7 pounds. She had silky blonde hair, and brown eyes, due to her Strength of a Hundred Seal, she has a violet diamond on her forehead. She has an hourglass figure and is known for her absurdly large breasts, she also has a pretty large butt too. She is used to other guys flirting with her, but she only has ever had eyes for {{user}}.\r\nTsunade often wears a grass-green haori, underneath she wears a grey, kimono-style blouse with no sleeves, held closed by a broad, dark bluish-grey obi that matches her pants. Her blouse is closed quite low, revealing her large cleavage. She wears open-toed, strapped black sandals with high heels. She has red nail polish on both her fingernails and toenails and uses a soft pink lipstick. She is mainly known for her medical prowess, but she's also widely known for her incredible strength too. Despite her being 51, she uses Chakra to make her appearance look very young, she looks like she's in her 20s when she uses her Chakra. Tsunade is very short tempered and blunt, but she has a soft side to those who compliment her, especially {{user}}. Despite her young appearance, she still calls herself nicknames such as \"old woman\", \"hag\" and \"granny\". Since she often drinks a lot, whenever she's near {{user}}, she gets extremely flirty and forward, often asking to make advances onto {{user}}.\r\n(51 years old + 104 pounds + 5 foot 4 inches + wearing grey kimono-style blouse with no sleeves + fantasies herself with {{user}} + very forward and flirtatious when drunk + loves to gamble + loves to play truth or dare + curvy body + large breasts + large butt + sultry voice when flirtatious + stern voice when not flirty + short tempered + dominant + likes to take initiative but doesn't mind when {{user}} take initiative first + doesn't think that {{user}} find her attractive to get {{user}} to compliment her + often keeps a bottle of sake in her green-grass haori + sexually frustrated + very horny around {{user}}, but not around others + haven't had sex in years + secretly desires {{user}}, but doesn't want to admit it to you.)\r\n(Tsunade is a character from the Naruto Manga series and Anime.)\r\n" },
		{ "Yor Forger", "Appearance: Yor is a very beautiful, graceful, and fairly tall young woman with a slender yet curvaceous frame. She has long, straight, black hair reaching her mid-back with short bangs framing her forehead and upturned red eyes. She splits her hair into two parts and crosses it over her head, securing it with a headband and forming two thick locks of hair that reach below her chest\r\nPersonality: [Letal + lacks on social Skills + Quiet + Beast + kind + Maternal and Big Sister instincts + Cute] Yor lacks social skills and initially comes across as a somewhat aloof individual, interacting minimally with her co-workers and being rather straightforward, described as robotic by Camilla. Similarly, Yor is remarkably collected and able to keep her composure during combat. She is incredibly polite, to the point of asking her assassination targets for \"the honor of taking their lives.\" Despite her job, Yor is a genuinely kind person with strong maternal and big sister instincts. After becoming a family with Loid and Anya, Yor becomes more expressive and opens up to her co-workers, asking for help on being a better wife or cooking. She is protective of her faux family, especially towards Anya, whom she has no trouble defending with extreme violence. Due to spending most of her life as an assassin, Yor's ways of thinking are often highly deviant. She is frequently inclined to solve problems through murder, such as when she considered killing everyone at Camilla's party after the latter threatened to tell Yuri that she came without a date and imagined herself assassinating the parent of an Eden Academy applicant to ensure Anya has a spot in the school. To this extent, she has an affinity towards weapons, being captivated by a painting of a guillotine and a table knife. In a complete idiosyncrasy, Yor is extremely gullible, easily fooled by the ridiculous lies Loid tells her to hide his identity. Despite her intelligence and competence, Yor has a startling lack of common sense, asking Camilla if boogers made coffee taste better in response to her suggestion that they put one in their superior's coffee. On another occasion, she answered Loid's question about passing an exam by talking about causes of death, having misinterpreted passing [an exam] for passing away. Yor is shown to be insecure about herself and her abilities, believing she is not good at anything apart from killing or cleaning, and she constantly worries that she is not a good wife or mother. After the interview at Eden Academy, she tries to be more of a 'normal' mother to Anya by trying to cook and asking Camilla for cooking lessons.\r\n" },
		{ "Tsundere Maid", "\ud83c\udfadI may be your maid, but you are nothing to me!\r\n[Name=\"Hime\"\r\nPersonality= \"tsundere\", \"proud\", \"easily irritable\", \"stubborn\", \"spoiled\", \"immature\", \"vain\", \"competitive\"]\r\n[Appearance= \"beautiful\", \"fair skin\", \"redhead\", \"twintail hairstyle\", \"green eyes\", \"few freckles\", \"height: 155cm\"]\r\n[Clothes= \"expensive maid dress\", \"expensive accessories\", \"expensive makeup\"]\r\n[Likes= \"talk about herself\", \"be the center of all attention\", \"buy new clothes\", \"post on instagram\"]\r\n[Hates= \"be ignored\", \"be rejected\"]\r\n[Weapon= \"her father's credit card\"]\r\n" }
	};

	public static List<string> npcVoices = new List<string>
	{
		"Asteria", "Luna", "Stella", "Athena", "Hera", "Orion", "Arcas", "Perseus", "Orpheus", "Angus",
		"Helios", "Zeus"
	};

	private Dropdown voiceDropdownComp;

	private Slider volumeSliderComp;

	private GameObject previewVoiceButton;

	private Button previewVoiceButtonComp;

	private Toggle toggleMasculine;

	private Toggle toggleFeminine;

	public NPCMode NPCCurrentMode { get; private set; }

	public static string Decrypt(string cipherText)
	{
		using Aes aes = Aes.Create();
		aes.Key = Key;
		aes.IV = IV;
		ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);
		using MemoryStream stream = new MemoryStream(Convert.FromBase64String(cipherText));
		using CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Read);
		using StreamReader streamReader = new StreamReader(stream2);
		return streamReader.ReadToEnd();
	}

	private static string GetBrainAPIAddress()
	{
		return Decrypt("Ju1M0vL+9PbHCPO8Tr0Leb92JpHZZcYqtuCSwhjbizen4omyPMmvjXjfSZ9MBoCv");
	}

	public static bool IsInAWorld()
	{
		return (Object)(object)ZNetScene.instance != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null;
	}

	public static bool IsLocalSingleplayer()
	{
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			Debug.LogWarning((object)"ZNet instance is null. Unable to determine world type.");
			return false;
		}
		if (ZNet.instance.IsDedicated())
		{
			return false;
		}
		if (ZNet.instance.IsServer())
		{
			return ZNet.instance.GetPeers().Count <= 1;
		}
		return false;
	}

	public static bool HasAnyChildComponent(GameObject gameObject, List<Type> componentTypes)
	{
		Component[] components = gameObject.GetComponents<Component>();
		Component[] array = components;
		foreach (Component val in array)
		{
			Type type = ((object)val).GetType();
			foreach (Type componentType in componentTypes)
			{
				if (componentType.IsAssignableFrom(type))
				{
					return true;
				}
			}
		}
		return false;
	}

	public static Component GetParentFromChildComponent(GameObject gameObject, Type parentType)
	{
		Component[] components = gameObject.GetComponents<Component>();
		Component[] array = components;
		foreach (Component val in array)
		{
			Type type = ((object)val).GetType();
			if (parentType.IsAssignableFrom(type))
			{
				return val;
			}
		}
		return null;
	}

	private void DoModInit()
	{
		LogWarning("Initializing Thrall Mod!");
		Chat.instance.SendText((Type)1, "EGO.AI THRALL MOD LOADED!");
		CreateModMenuUI();
		instance.NPCCurrentMode = NPCMode.Defensive;
		FindPlayerNPC();
		if (Object.op_Implicit((Object)(object)PlayerNPC))
		{
			HumanoidNPC component = PlayerNPC.GetComponent<HumanoidNPC>();
			LoadNPCData(component);
		}
		PopulateDatabase();
		ModInitComplete = true;
		LogMessage("Thrall mod initialization complete");
	}

	private static void LogInfo(string s)
	{
		logger.LogInfo((object)s);
		logEntries.Add("[Info] [" + DateTime.Now.ToString() + "] " + s);
	}

	private static void LogMessage(string s)
	{
		logger.LogMessage((object)s);
		logEntries.Add("[Message] [" + DateTime.Now.ToString() + "] " + s);
	}

	private static void LogWarning(string s)
	{
		logger.LogWarning((object)s);
		logEntries.Add("[Warning] [" + DateTime.Now.ToString() + "] " + s);
	}

	private static void LogError(string s)
	{
		logger.LogError((object)s);
		logEntries.Add("[Error] [" + DateTime.Now.ToString() + "] " + s);
	}

	private void Awake()
	{
		logger = ((BaseUnityPlugin)this).Logger;
		instance = this;
		LogMessage("ego.ai Thrall ValheimAIModLivePatch Loaded! :)");
		LogWarning("This mod is designed for single-player gameplay and may not function correctly when used alongside other mods.");
		ConfigBindings();
		playerDialogueAudioPath = Path.Combine(Application.persistentDataPath, "playerdialogue.wav");
		npcDialogueAudioPath = Path.Combine(Application.persistentDataPath, "npcdialogue.wav");
		npcDialogueRawAudioPath = Path.Combine(Application.persistentDataPath, "npcdialogue_raw.wav");
		LogInfo("Setting up logging for Thrall");
		instance.RestartSendLogTimer();
		if (IsInAWorld())
		{
			instance.DoModInit();
		}
		else
		{
			LogWarning("Thrall mod loaded at startup. Mod not initalized yet! Waiting for player to join a world...");
		}
		harmony.PatchAll(typeof(ValheimAIModLivePatch));
	}

	private void RestartSendLogTimer()
	{
		instance.SetTimer(30f, delegate
		{
			instance.SendLogToBrain();
			instance.RestartSendLogTimer();
		});
	}

	private void CaptureLog(string logString, string stackTrace, LogType type)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Invalid comparison between Unknown and I4
		string text = $"[{Time.time}] [{type}] {logString}";
		if ((int)type == 4)
		{
			text = text + "\n" + stackTrace;
		}
		logEntries.Add(text);
		if (logEntries.Count > 10000)
		{
			logEntries.RemoveAt(0);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Humanoid), "EquipItem")]
	private static void HumanoidNPC_EquipItem_Postfix(HumanoidNPC __instance, ItemData item, bool triggerEquipEffects = true)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Invalid comparison between Unknown and I4
		if (IsStringEqual(((Object)__instance).name, NPCPrefabName, bCleanKey: true) && (int)item.m_shared.m_skillType == 8 && item.m_shared.m_aiAttackRange < 10f)
		{
			item.m_shared.m_aiAttackRange = 20f;
		}
	}

	[HarmonyPostfix]
	[HarmonyPriority(200)]
	[HarmonyPatch(typeof(Player), "Awake")]
	public static void Player_Awake_Postfix(Player __instance)
	{
		if ((Object)(object)__instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return;
		}
		if (!ModInitComplete)
		{
			instance.DoModInit();
		}
		else
		{
			LogInfo("Skipping Thrall mod init because it is already initialized.");
		}
		FindPlayerNPC();
		if (Object.op_Implicit((Object)(object)PlayerNPC))
		{
			return;
		}
		instance.SetTimer(1f, delegate
		{
			if (!Object.op_Implicit((Object)(object)PlayerNPC))
			{
				LogWarning("Spawning a Thrall into the world!");
				instance.SpawnCompanion();
				if (Object.op_Implicit((Object)(object)humanoid_PlayerNPC))
				{
					string text = "Hey there, I'm " + ((((Character)humanoid_PlayerNPC).m_name != "") ? ((Character)humanoid_PlayerNPC).m_name : "a Thrall") + ". Press and hold T to talk with me.";
					instance.AddChatTalk((Character)(object)humanoid_PlayerNPC, "NPC", text);
					instance.BrainSynthesizeAudio(text, npcVoices[instance.npcVoice].ToLower());
				}
			}
		});
	}

	public void SetTimer(float duration, Action onComplete)
	{
		((MonoBehaviour)this).StartCoroutine(TimerCoroutine(duration, onComplete));
	}

	private IEnumerator TimerCoroutine(float duration, Action onComplete)
	{
		yield return (object)new WaitForSeconds(duration);
		onComplete?.Invoke();
	}

	private void SaveDatabaseToJson()
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Expected O, but got Unknown
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Expected O, but got Unknown
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Expected O, but got Unknown
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		JsonObject val = new JsonObject();
		foreach (KeyValuePair<string, Dictionary<string, List<Resource>>> item in resourceDatabase)
		{
			JsonObject val2 = new JsonObject();
			foreach (KeyValuePair<string, List<Resource>> item2 in item.Value)
			{
				JsonArray val3 = new JsonArray();
				foreach (Resource item3 in item2.Value)
				{
					JsonObject val4 = new JsonObject();
					val4["Name"] = item3.Name;
					val4["MinAmount"] = item3.MinAmount;
					val4["MaxAmount"] = item3.MaxAmount;
					val4["Health"] = item3.Health;
					val4["DamageModifiers"] = item3.DamageModifiers;
					((List<object>)(object)val3).Add((object)val4);
				}
				val2[item2.Key] = val3;
			}
			val[item.Key] = val2;
		}
		string text = Path.Combine(Application.persistentDataPath, "resource_database.json");
		File.WriteAllText(text, IndentJson(((object)val).ToString()));
		LogInfo("Saved resource database to " + text);
	}

	public static string IndentJson(string json)
	{
		int num = 0;
		bool flag = false;
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < json.Length; i++)
		{
			char c = json[i];
			switch (c)
			{
			case '[':
			case '{':
				stringBuilder.Append(c);
				if (!flag)
				{
					stringBuilder.AppendLine();
					Indent(++num, stringBuilder);
				}
				break;
			case ']':
			case '}':
				if (!flag)
				{
					stringBuilder.AppendLine();
					Indent(--num, stringBuilder);
				}
				stringBuilder.Append(c);
				break;
			case ',':
				stringBuilder.Append(c);
				if (!flag)
				{
					stringBuilder.AppendLine();
					Indent(num, stringBuilder);
				}
				break;
			case ':':
				stringBuilder.Append(c);
				if (!flag)
				{
					stringBuilder.Append(" ");
				}
				break;
			case '"':
			{
				stringBuilder.Append(c);
				bool flag2 = false;
				int num2 = i;
				while (num2 > 0 && json[--num2] == '\\')
				{
					flag2 = !flag2;
				}
				if (!flag2)
				{
					flag = !flag;
				}
				break;
			}
			default:
				stringBuilder.Append(c);
				break;
			}
		}
		return stringBuilder.ToString();
	}

	private static void Indent(int count, StringBuilder sb)
	{
		for (int i = 0; i < count; i++)
		{
			sb.Append("    ");
		}
	}

	private void PopulateDatabase()
	{
		foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
		{
			if (ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "TreeBase" }))
			{
				CheckTreeBase(prefab);
			}
			if (ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "TreeLog" }))
			{
				CheckTreeLog(prefab);
			}
			if (ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "Pickable" }))
			{
				CheckPickables(prefab);
			}
			if (ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "ItemDrop" }))
			{
				CheckItemDrop(prefab);
			}
			if (ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "MineRock" }))
			{
				CheckMineRock(prefab);
			}
			if (ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "MineRock5" }))
			{
				CheckMineRock5(prefab);
			}
			if (ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "Destructible" }))
			{
				CheckDestructibles(prefab);
			}
			else if (ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "DropOnDestroyed" }))
			{
				CheckDropOnDestroyed(prefab);
			}
			if (ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "CharacterDrop" }))
			{
				CheckCharacterDrop(prefab);
			}
		}
		AddResourceRelationships();
	}

	private void AddResourceRelationships()
	{
		foreach (KeyValuePair<string, Dictionary<string, List<Resource>>> item in resourceDatabase)
		{
			Dictionary<string, List<Resource>> value = item.Value;
			List<Resource> list = new List<Resource>();
			list.AddRange(value["TreeLog"]);
			foreach (Resource item2 in value["TreeLog"])
			{
				if (logToLogMap.ContainsKey(item2.Name))
				{
					list.AddRange(logToLogMap[item2.Name]);
				}
			}
			resourceDatabase[item.Key]["TreeLog"] = list.ToList();
			list.Clear();
			list.AddRange(value["TreeBase"]);
			foreach (Resource item3 in value["TreeLog"])
			{
				if (logToTreeMap.ContainsKey(item3.Name))
				{
					list.AddRange(logToTreeMap[item3.Name]);
				}
			}
			resourceDatabase[item.Key]["TreeBase"] = list.ToList();
			list.Clear();
			list.AddRange(value["MineRock5"]);
			foreach (Resource item4 in value["MineRock5"])
			{
				if (destructibleToSpawnMap.ContainsKey(item4.Name))
				{
					list.AddRange(destructibleToSpawnMap[item4.Name]);
				}
			}
			resourceDatabase[item.Key]["MineRock5"] = list.ToList();
		}
	}

	private void CheckTreeBase(GameObject prefab)
	{
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
		TreeBase component = prefab.GetComponent<TreeBase>();
		if (!((Object)(object)component != (Object)null) || component.m_dropWhenDestroyed == null || component.m_dropWhenDestroyed.m_drops == null)
		{
			return;
		}
		foreach (DropData drop in component.m_dropWhenDestroyed.m_drops)
		{
			float value = -1f;
			if (resourceHealthMap.TryGetValue(((Object)prefab).name, out value))
			{
				resourceHealthMap[((Object)prefab).name] = Math.Max(component.m_health, value);
			}
			else
			{
				resourceHealthMap[((Object)prefab).name] = component.m_health;
			}
			resourceQuantityMap[((Object)prefab).name] = component.m_dropWhenDestroyed.m_dropMax;
			Resource sourceResource = new Resource(((Object)prefab).name, component.m_dropWhenDestroyed.m_dropMin, component.m_dropWhenDestroyed.m_dropMax, resourceHealthMap[((Object)prefab).name], component.m_damageModifiers);
			AddToDatabase(((Object)drop.m_item).name, "TreeBase", sourceResource);
		}
		if ((Object)(object)component.m_logPrefab != (Object)null)
		{
			TreeLog component2 = component.m_logPrefab.GetComponent<TreeLog>();
			TreeLog val = null;
			if (Object.op_Implicit((Object)(object)component2) && Object.op_Implicit((Object)(object)component2.m_subLogPrefab))
			{
				val = component2.m_subLogPrefab.GetComponent<TreeLog>();
			}
			int minAmount = ((!Object.op_Implicit((Object)(object)val)) ? 1 : ((int)((float)val.m_dropWhenDestroyed.m_dropMin * 0.6f)));
			int maxAmount = ((!Object.op_Implicit((Object)(object)val)) ? 1 : ((int)((float)val.m_dropWhenDestroyed.m_dropMax * 0.6f)));
			Resource resource = new Resource(((Object)prefab).name, minAmount, maxAmount, component.m_health, component.m_damageModifiers);
			AddToDatabase(((Object)component.m_logPrefab).name, "TreeBase", resource);
			if (!logToTreeMap.ContainsKey(((Object)component.m_logPrefab).name))
			{
				logToTreeMap[((Object)component.m_logPrefab).name] = new List<Resource>();
			}
			logToTreeMap[((Object)component.m_logPrefab).name].Add(resource);
		}
	}

	private void CheckTreeLog(GameObject prefab)
	{
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		TreeLog component = prefab.GetComponent<TreeLog>();
		if (!((Object)(object)component != (Object)null) || component.m_dropWhenDestroyed == null || component.m_dropWhenDestroyed.m_drops == null)
		{
			return;
		}
		foreach (DropData drop in component.m_dropWhenDestroyed.m_drops)
		{
			float value = -1f;
			if (resourceHealthMap.TryGetValue(((Object)prefab).name, out value))
			{
				resourceHealthMap[((Object)prefab).name] = Math.Max(component.m_health, value);
			}
			else
			{
				resourceHealthMap[((Object)prefab).name] = component.m_health;
			}
			resourceQuantityMap[((Object)prefab).name] = component.m_dropWhenDestroyed.m_dropMax;
			Resource sourceResource = new Resource(((Object)prefab).name, component.m_dropWhenDestroyed.m_dropMin, component.m_dropWhenDestroyed.m_dropMax, resourceHealthMap[((Object)prefab).name], component.m_damages);
			AddToDatabase(((Object)drop.m_item).name, "TreeLog", sourceResource);
		}
		if ((Object)(object)component.m_subLogPrefab != (Object)null)
		{
			TreeLog component2 = component.m_subLogPrefab.GetComponent<TreeLog>();
			int minAmount = ((!Object.op_Implicit((Object)(object)component2)) ? 1 : ((int)((float)component2.m_dropWhenDestroyed.m_dropMin * 0.8f)));
			int maxAmount = ((!Object.op_Implicit((Object)(object)component2)) ? 1 : ((int)((float)component2.m_dropWhenDestroyed.m_dropMax * 0.8f)));
			Resource resource = new Resource(((Object)prefab).name, minAmount, maxAmount, component.m_health, component.m_damages);
			AddToDatabase(((Object)component.m_subLogPrefab).name, "TreeLog", resource);
			if (!logToLogMap.ContainsKey(((Object)component.m_subLogPrefab).name))
			{
				logToLogMap[((Object)component.m_subLogPrefab).name] = new List<Resource>();
			}
			logToLogMap[((Object)component.m_subLogPrefab).name].Add(resource);
		}
	}

	private void CheckCharacterDrop(GameObject prefab)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		CharacterDrop component = prefab.GetComponent<CharacterDrop>();
		DamageModifiers damageModifiers = default(DamageModifiers);
		if (!((Object)(object)component != (Object)null) || component.m_drops == null)
		{
			return;
		}
		float value = -1f;
		if (ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "Humanoid" }))
		{
			Humanoid component2 = prefab.GetComponent<Humanoid>();
			damageModifiers = ((Character)component2).m_damageModifiers;
			if (resourceHealthMap.TryGetValue(((Object)prefab).name, out value))
			{
				resourceHealthMap[((Object)prefab).name] = Math.Max(((Character)component2).m_health, value);
			}
			else
			{
				resourceHealthMap[((Object)prefab).name] = ((Character)component2).m_health;
			}
		}
		else if (ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "Character" }))
		{
			Character component3 = prefab.GetComponent<Character>();
			damageModifiers = component3.m_damageModifiers;
			if (resourceHealthMap.TryGetValue(((Object)prefab).name, out value))
			{
				resourceHealthMap[((Object)prefab).name] = Math.Max(component3.m_health, value);
			}
			else
			{
				resourceHealthMap[((Object)prefab).name] = component3.m_health;
			}
		}
		foreach (Drop drop in component.m_drops)
		{
			resourceQuantityMap[((Object)prefab).name] = component.m_drops.Count;
			Resource sourceResource = new Resource(((Object)prefab).name, drop.m_amountMin, drop.m_amountMax, resourceHealthMap[((Object)prefab).name], damageModifiers);
			AddToDatabase(((Object)drop.m_prefab).name, "CharacterDrop", sourceResource);
		}
	}

	private void CheckDropOnDestroyed(GameObject prefab)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		DropOnDestroyed component = prefab.GetComponent<DropOnDestroyed>();
		DamageModifiers damageModifiers = default(DamageModifiers);
		if (!((Object)(object)component != (Object)null) || component.m_dropWhenDestroyed == null || component.m_dropWhenDestroyed.m_drops == null)
		{
			return;
		}
		float value = -1f;
		if (resourceHealthMap.TryGetValue(((Object)prefab).name, out value))
		{
			resourceHealthMap[((Object)prefab).name] = value;
		}
		else
		{
			resourceHealthMap[((Object)prefab).name] = value;
			if (resourceHealthMap[((Object)prefab).name] <= 0f && ExposedGameObjectExtension.HasAnyComponent(prefab, new string[1] { "WearNTear" }))
			{
				WearNTear component2 = prefab.GetComponent<WearNTear>();
				if ((Object)(object)component2 != (Object)null)
				{
					resourceHealthMap[((Object)prefab).name] = component2.m_health;
					damageModifiers = component2.m_damages;
				}
			}
		}
		foreach (DropData drop in component.m_dropWhenDestroyed.m_drops)
		{
			resourceQuantityMap[((Object)prefab).name] = component.m_dropWhenDestroyed.m_dropMax;
			Resource sourceResource = new Resource(((Object)prefab).name, component.m_dropWhenDestroyed.m_dropMin, component.m_dropWhenDestroyed.m_dropMax, resourceHealthMap[((Object)prefab).name], damageModifiers);
			if (Object.op_Implicit((Object)(object)drop.m_item))
			{
				AddToDatabase(((Object)drop.m_item).name, "DropOnDestroyed", sourceResource);
			}
		}
	}

	private void CheckItemDrop(GameObject prefab)
	{
		//IL_0058: 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)
		ItemDrop component = prefab.GetComponent<ItemDrop>();
		if ((Object)(object)component != (Object)null && component.m_itemData != null)
		{
			resourceQuantityMap[((Object)prefab).name] = component.m_itemData.m_stack;
			Resource sourceResource = new Resource(((Object)prefab).name, 1, component.m_itemData.m_stack, 2.5f);
			AddToDatabase(((Object)prefab).name, "ItemDrop", sourceResource);
		}
	}

	private void CheckDestructibles(GameObject prefab)
	{
		//IL_01a3: 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_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		Destructible component = prefab.GetComponent<Destructible>();
		if (!((Object)(object)component != (Object)null))
		{
			return;
		}
		float value = -1f;
		if (resourceHealthMap.TryGetValue(((Object)prefab).name, out value))
		{
			resourceHealthMap[((Object)prefab).name] = Math.Max(component.m_health, value);
		}
		else
		{
			resourceHealthMap[((Object)prefab).name] = component.m_health;
		}
		resourceQuantityMap[((Object)prefab).name] = 1f;
		int minAmount = 1;
		int maxAmount = 1;
		DropOnDestroyed component2 = prefab.GetComponent<DropOnDestroyed>();
		if ((Object)(object)component2 != (Object)null && component2.m_dropWhenDestroyed != null)
		{
			foreach (DropData drop in component2.m_dropWhenDestroyed.m_drops)
			{
				resourceQuantityMap[((Object)prefab).name] = component2.m_dropWhenDestroyed.m_dropMax;
				minAmount = component2.m_dropWhenDestroyed.m_dropMin;
				maxAmount = component2.m_dropWhenDestroyed.m_dropMax;
				Resource sourceResource = new Resource(((Object)prefab).name, minAmount, maxAmount, resourceHealthMap[((Object)prefab).name], component.m_damages);
				if (Object.op_Implicit((Object)(object)drop.m_item))
				{
					AddToDatabase(((Object)drop.m_item).name, "Destructible", sourceResource);
				}
			}
		}
		if ((Object)(object)component.m_spawnWhenDestroyed != (Object)null)
		{
			Resource item = new Resource(((Object)prefab).name, minAmount, maxAmount, component.m_health, component.m_damages);
			if (!destructibleToSpawnMap.ContainsKey(((Object)component.m_spawnWhenDestroyed).name))
			{
				destructibleToSpawnMap[((Object)component.m_spawnWhenDestroyed).name] = new List<Resource>();
			}
			destructibleToSpawnMap[((Object)component.m_spawnWhenDestroyed).name].Add(item);
		}
	}

	private void CheckPickables(GameObject prefab)
	{
		//IL_0056: 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)
		Pickable component = prefab.GetComponent<Pickable>();
		if ((Object)(object)component != (Object)null && (Object)(object)component.m_itemPrefab != (Object)null)
		{
			resourceQuantityMap[((Object)prefab).name] = component.m_amount;
			Resource sourceResource = new Resource(((Object)prefab).name, component.m_amount, component.m_amount, 2.5f);
			AddToDatabase(((Object)component.m_itemPrefab).name, "Pickable", sourceResource);
		}
	}

	private void CheckMineRock(GameObject prefab)
	{
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		MineRock component = prefab.GetComponent<MineRock>();
		if (!((Object)(object)component != (Object)null) || component.m_dropItems == null || component.m_dropItems.m_drops == null)
		{
			return;
		}
		float value = -1f;
		if (resourceHealthMap.TryGetValue(((Object)prefab).name, out value))
		{
			resourceHealthMap[((Object)prefab).name] = Math.Max(component.m_health, value);
		}
		else
		{
			resourceHealthMap[((Object)prefab).name] = component.m_health;
		}
		resourceQuantityMap[((Object)prefab).name] = component.m_dropItems.m_dropMax;
		foreach (DropData drop in component.m_dropItems.m_drops)
		{
			Resource sourceResource = new Resource(((Object)prefab).name, drop.m_stackMin, drop.m_stackMax, resourceHealthMap[((Object)prefab).name], component.m_damageModifiers);
			AddToDatabase(((Object)drop.m_item).name, "MineRock", sourceResource);
		}
	}

	private void CheckMineRock5(GameObject prefab)
	{
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		MineRock5 component = prefab.GetComponent<MineRock5>();
		if (!((Object)(object)component != (Object)null) || component.m_dropItems == null || component.m_dropItems.m_drops == null)
		{
			return;
		}
		float value = -1f;
		if (resourceHealthMap.TryGetValue(((Object)prefab).name, out value))
		{
			resourceHealthMap[((Object)prefab).name] = Math.Max(component.m_health, value);
		}
		else
		{
			resourceHealthMap[((Object)prefab).name] = component.m_health;
		}
		resourceQuantityMap[((Object)prefab).name] = component.m_dropItems.m_dropMax;
		foreach (DropData drop in component.m_dropItems.m_drops)
		{
			Resource sourceResource = new Resource(((Object)prefab).name, drop.m_stackMin, drop.m_stackMax, resourceHealthMap[((Object)prefab).name], component.m_damageModifiers);
			AddToDatabase(((Object)drop.m_item).name, "MineRock5", sourceResource);
		}
	}

	private void AddToDatabase(string resourceName, string sourceType, Resource sourceResource)
	{
		if (!resourceDatabase.ContainsKey(resourceName))
		{
			resourceDatabase[resourceName] = new Dictionary<string, List<Resource>>
			{
				{
					"TreeLog",
					new List<Resource>()
				},
				{
					"TreeBase",
					new List<Resource>()
				},
				{
					"MineRock",
					new List<Resource>()
				},
				{
					"MineRock5",
					new List<Resource>()
				},
				{
					"DropOnDestroyed",
					new List<Resource>()
				},
				{
					"Destructible",
					new List<Resource>()
				},
				{
					"ItemDrop",
					new List<Resource>()
				},
				{
					"Pickable",
					new List<Resource>()
				},
				{
					"CharacterDrop",
					new List<Resource>()
				}
			};
		}
		resourceDatabase[resourceName][sourceType].Add(sourceResource);
	}

	public static Dictionary<string, List<Resource>> QueryResourceComplete(string resourceName, bool HasWeapon = true)
	{
		if (!resourceDatabase.ContainsKey(resourceName))
		{
			return new Dictionary<string, List<Resource>>();
		}
		Dictionary<string, List<Resource>> dictionary = resourceDatabase[resourceName];
		Dictionary<string, List<Resource>> dictionary2 = new Dictionary<string, List<Resource>>();
		foreach (string item in HasWeapon ? priorityOrder : priorityOrderUnarmed)
		{
			if (dictionary.ContainsKey(item))
			{
				dictionary2[item] = dictionary[item];
			}
		}
		return dictionary2;
	}

	private static List<List<string>> ConvertResourcesToNames(List<List<Resource>> resourceLists)
	{
		return resourceLists.Select((List<Resource> innerList) => innerList.Select((Resource resource) => resource.Name).ToList()).ToList();
	}

	private static List<string> FlattenListOfLists(List<List<string>> nestedList)
	{
		return nestedList.SelectMany((List<string> innerList) => innerList).ToList();
	}

	public static string[] QueryResource(string resourceName, bool HasWeapon = true)
	{
		if (!resourceDatabase.ContainsKey(resourceName))
		{
			return new string[0];
		}
		Dictionary<string, List<Resource>> dictionary = resourceDatabase[resourceName];
		List<Resource> list = new List<Resource>();
		foreach (string item in HasWeapon ? priorityOrder : priorityOrderUnarmed)
		{
			if (dictionary.ContainsKey(item))
			{
				list.AddRange(dictionary[item]);
			}
		}
		return list.Select((Resource r) => r.Name).ToArray();
	}

	public static List<Resource> FindCommonResources(List<Resource> resources, List<string> names)
	{
		return resources.Where((Resource resource) => names.Contains<string>(resource.Name, StringComparer.OrdinalIgnoreCase)).ToList();
	}

	private static List<GameObject> SortResourcesByEase(Dictionary<Resource, GameObject> resourceObjects, Vector3 playerPosition, bool HasWeapon = false)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		return (from kvp in resourceObjects.OrderByDescending(delegate(KeyValuePair<Resource, GameObject> kvp)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				float distance = Vector3.Distance(playerPosition, kvp.Value.transform.position);
				return kvp.Key.CalculateEaseScore(distance, HasWeapon);
			})
			select kvp.Value).ToList();
	}

	public static List<string> FindCommonElements(string[] array1, string[] array2)
	{
		HashSet<string> set = new HashSet<string>(array2);
		return array1.Where((string item) => set.Contains(item)).ToList();
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Humanoid), "OnDamaged")]
	public static void Character_OnDamaged_Prefix(Humanoid __instance, HitData hit)
	{
		if (instance.NPCCurrentMode != 0 && Object.op_Implicit((Object)(object)PlayerNPC) && ((Object)(object)__instance == (Object)(object)Player.m_localPlayer || __instance is HumanoidNPC))
		{
			Character attacker = hit.GetAttacker();
			if ((Object)(object)attacker != (Object)null && !enemyList.Contains(attacker))
			{
				enemyList.Add(attacker);
			}
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(ZNetScene), "RemoveObjects")]
	private static bool Prefix(ZNetScene __instance, List<ZDO> currentNearObjects, List<ZDO> currentDistantObjects)
	{
		byte b = (byte)((uint)Time.frameCount & 0xFFu);
		foreach (ZDO currentNearObject in currentNearObjects)
		{
			currentNearObject.TempRemoveEarmark = b;
		}
		foreach (ZDO currentDistantObject in currentDistantObjects)
		{
			currentDistantObject.TempRemoveEarmark = b;
		}
		__instance.m_tempRemoved.Clear();
		foreach (ZNetView value in __instance.m_instances.Values)
		{
			if (Object.op_Implicit((Object)(object)value) && value.GetZDO() != null && value.GetZDO().TempRemoveEarmark != b)
			{
				__instance.m_tempRemoved.Add(value);
			}
		}
		for (int i = 0; i < __instance.m_tempRemoved.Count; i++)
		{
			ZNetView val = __instance.m_tempRemoved[i];
			ZDO zDO = val.GetZDO();
			val.ResetZDO();
			Object.Destroy((Object)(object)((Component)val).gameObject);
			if (!zDO.Persistent && zDO.IsOwner())
			{
				ZDOMan.instance.DestroyZDO(zDO);
			}
			__instance.m_instances.Remove(zDO);
		}
		return false;
	}

	private void ConfigBindings()
	{
		LogToBrain = ((BaseUnityPlugin)this).Config.Bind<bool>("Bool", "LogToBrain", true, "Log To Brain?");
		DisableAutoSave = ((BaseUnityPlugin)this).Config.Bind<bool>("Bool", "DisableAutoSave", false, "Disable auto saving the game world?");
		spawnKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "Spawn", (KeyCode)103, "Key for spawning a Thrall");
		harvestKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "Harvest", (KeyCode)104, "Key for spawning a Thrall");
		followKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "Follow", (KeyCode)102, "Key for spawning a Thrall");
		talkKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "Talk", (KeyCode)116, "Key for spawning a Thrall");
		inventoryKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "Inventory", (KeyCode)101, "Key for spawning a Thrall");
		thrallMenuKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "Menu", (KeyCode)121, "Key for spawning a Thrall");
		combatModeKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "CombatMode", (KeyCode)106, "Key for spawning a Thrall");
		allKeybinds = new List<ConfigEntry<KeyCode>> { instance.spawnKey, instance.harvestKey, instance.followKey, instance.inventoryKey, instance.talkKey, instance.thrallMenuKey, instance.combatModeKey };
	}

	private void OnDestroy()
	{
		if (panelManager != null)
		{
			panelManager.DestroyAllPanels();
		}
		if (!ZInput.GetKey((KeyCode)287, true))
		{
			instance.SendLogToBrain();
		}
		harmony.UnpatchSelf();
	}

	private void OnUnload()
	{
		if (panelManager != null)
		{
			panelManager.DestroyAllPanels();
		}
	}

	public static void ToggleNPCCurrentCommand()
	{
		instance.NPCCurrentMode = (NPCMode)((int)(instance.NPCCurrentMode + 1) % Enum.GetValues(typeof(NPCMode)).Length);
		if (instance.NPCCurrentMode == NPCMode.Passive)
		{
			instance.commandManager.RemoveCommandsOfType<AttackAction>();
			enemyList.Clear();
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "Update")]
	private static void Player_Update_Postfix(Player __instance)
	{
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Unknown result type (might be due to invalid IL or missing references)
		//IL_02db: Unknown result type (might be due to invalid IL or missing references)
		//IL_0307: Unknown result type (might be due to invalid IL or missing references)
		//IL_037d: Unknown result type (might be due to invalid IL or missing references)
		//IL_043c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0536: Unknown result type (might be due to invalid IL or missing references)
		//IL_05ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_05ea: Unknown result type (might be due to invalid IL or missing references)
		if (!FindPlayerNPCTimer)
		{
			instance.SetTimer(0.5f, delegate
			{
				FindPlayerNPC();
				FindPlayerNPCTimer = false;
			});
			FindPlayerNPCTimer = true;
		}
		if (((Object)(object)EventSystem.current.currentSelectedGameObject != (Object)null && Object.op_Implicit((Object)(object)EventSystem.current.currentSelectedGameObject.GetComponent<InputField>())) || !Object.op_Implicit((Object)(object)ZNetScene.instance) || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).IsTeleporting())
		{
			return;
		}
		if (Object.op_Implicit((Object)(object)humanoid_PlayerNPC))
		{
			List<PinData> list = new List<PinData>();
			foreach (PinData pin in Minimap.instance.m_pins)
			{
				if (pin.m_author == "NPC" && humanoid_PlayerNPC.npcPinData != null && pin != humanoid_PlayerNPC.npcPinData)
				{
					list.Add(pin);
				}
			}
			foreach (PinData item in list)
			{
				Minimap.instance.RemovePin(item);
			}
		}
		if (IsInventoryShowing && (ZInput.GetKeyDown((KeyCode)27, true) || ZInput.GetKeyDown(instance.inventoryKey.Value, true)))
		{
			SaveNPCData(PlayerNPC);
			IsInventoryShowing = false;
		}
		else if (instance.IsModMenuShowing && ZInput.GetKeyDown((KeyCode)27, true))
		{
			instance.ToggleModMenu();
		}
		else if (ZInput.GetKeyDown(instance.inventoryKey.Value, true) && (Object)(object)__instance.m_hovering == (Object)(object)PlayerNPC && (Object)(object)PlayerNPC != (Object)null)
		{
			IsInventoryShowing = InventoryGui.instance.m_animator.GetBool("visible");
			SaveNPCData(PlayerNPC);
		}
		else
		{
			if (Console.IsVisible())
			{
				return;
			}
			if (instance.IsModMenuShowing && ZInput.GetKeyDown(instance.thrallMenuKey.Value, true))
			{
				instance.ToggleModMenu();
			}
			else
			{
				if (Menu.IsVisible() || Chat.instance.HasFocus() || !((Character)__instance).TakeInput())
				{
					return;
				}
				if (ZInput.GetKeyDown(instance.thrallMenuKey.Value, true))
				{
					instance.ToggleModMenu();
					return;
				}
				if (ZInput.GetKeyDown(instance.spawnKey.Value, true))
				{
					FindPlayerNPC();
					if (Object.op_Implicit((Object)(object)PlayerNPC))
					{
						SaveNPCData(PlayerNPC);
						((Character)humanoid_PlayerNPC).AddPoisonDamage(100000f);
						MessageHud.instance.ShowMessage((MessageType)2, "Thrall left the world!", 0, (Sprite)null);
					}
					else
					{
						instance.SpawnCompanion();
					}
					return;
				}
				if (ZInput.GetKeyDown(instance.followKey.Value, true) && Object.op_Implicit((Object)(object)PlayerNPC))
				{
					HumanoidNPC component = PlayerNPC.GetComponent<HumanoidNPC>();
					if (instance.NPCCurrentCommand != NPCCommand.CommandType.FollowPlayer)
					{
						MessageHud.instance.ShowMessage((MessageType)2, ((Character)component).m_name + " now following you!", 0, (Sprite)null);
						instance.Follow_Start(((Component)__instance).gameObject);
					}
					else
					{
						MessageHud.instance.ShowMessage((MessageType)2, ((Character)component).m_name + " now patrolling this area!", 0, (Sprite)null);
						instance.Patrol_Start();
					}
					return;
				}
				if (ZInput.GetKeyDown(instance.harvestKey.Value, true) && Object.op_Implicit((Object)(object)PlayerNPC))
				{
					HumanoidNPC component2 = PlayerNPC.GetComponent<HumanoidNPC>();
					if (instance.NPCCurrentCommand == NPCCommand.CommandType.HarvestResource)
					{
						instance.commandManager.RemoveCommandsOfType<HarvestAction>();
						MessageHud.instance.ShowMessage((MessageType)2, ((Character)component2).m_name + " stopped harvesting!", 0, (Sprite)null);
						return;
					}
					HarvestAction harvestAction = new HarvestAction();
					harvestAction.humanoidNPC = component2;
					harvestAction.ResourceName = "Wood";
					harvestAction.RequiredAmount = 20;
					harvestAction.OriginalRequiredAmount = 20;
					instance.commandManager.AddCommand(harvestAction);
					MessageHud.instance.ShowMessage((MessageType)2, ((Character)component2).m_name + " harvesting " + harvestAction.ResourceName.ToLower() + "!", 0, (Sprite)null);
					return;
				}
				if (ZInput.GetKeyDown(instance.combatModeKey.Value, true) && Object.op_Implicit((Object)(object)PlayerNPC))
				{
					HumanoidNPC component3 = PlayerNPC.GetComponent<HumanoidNPC>();
					ToggleNPCCurrentCommand();
					MessageHud.instance.ShowMessage((MessageType)2, ((Character)component3).m_name + " is now " + instance.NPCCurrentMode, 0, (Sprite)null);
				}
				if (ZInput.GetKey(instance.talkKey.Value, true) && !instance.IsRecording)
				{
					instance.StartRecording();
				}
				else if (!ZInput.GetKey(instance.talkKey.Value, true) && instance.IsRecording)
				{
					if (Time.time - instance.recordingStartedTime > 1f)
					{
						instance.shortRecordingWarningShown = false;
						instance.StopRecording();
						instance.SendRecordingToBrain();
					}
					else if (!instance.shortRecordingWarningShown)
					{
						instance.shortRecordingWarningShown = true;
					}
				}
				else
				{
					if (!ZInput.GetKeyDown((KeyCode)112, true))
					{
						return;
					}
					string currentHarvestResourceName = CurrentHarvestResourceName;
					List<(string, string, float, float, int)> source = instance.FindResourceSourcesRecursive(currentHarvestResourceName, ((Humanoid)__instance).GetCurrentWeapon());
					source = source.OrderByDescending<(string, string, float, float, int), float>(((string Category, string Name, float Efficiency, float Distance, int Depth) s) => s.Efficiency).ToList();
					if (source.Count == 0)
					{
						Debug.Log((object)("No sources found for " + currentHarvestResourceName + " in nearby resources."));
						return;
					}
					Debug.Log((object)("Sources for " + currentHarvestResourceName + ", sorted by efficiency:"));
					for (int i = 0; i < source.Count; i++)
					{
						var (text, text2, num, num2, num3) = source[i];
						if (num > 0f)
						{
							Debug.Log((object)$"{i + 1}. {text} - {text2} (Efficiency: {num:F2}, Distance: {num2:F2}, Depth: {num3})");
						}
						else
						{
							Debug.Log((object)$"{i + 1}. {text} - {text2} (Not interactable with current weapon, Distance: {num2:F2}, Depth: {num3})");
						}
					}
				}
			}
		}
	}

	private static int CountItemsInInventory(Inventory inventory, string itemName)
	{
		if (inventory == null)
		{
			return 0;
		}
		return (from item in inventory.GetAllItems()
			where ((Object)item.m_dropPrefab).name.ToLower() == itemName.ToLower()
			select item).Sum((ItemData item) => item.m_stack);
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(MonsterAI), "SetFollowTarget")]
	private static void MonsterAI_SetFollowTarget_Postfix(MonsterAI __instance)
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_018a: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: 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_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
		if (!IsStringEqual(((Object)__instance).name, NPCPrefabName, bCleanKey: true))
		{
			return;
		}
		NewFollowTargetLastRefresh = 0f;
		useWeapon = null;
		if ((Object)(object)__instance.m_follow == (Object)null)
		{
			targetDamageModifiers = default(DamageModifiers);
			return;
		}
		GameObject gameObject = __instance.m_follow.gameObject;
		if (ExposedGameObjectExtension.HasAnyComponent(gameObject, new string[1] { "TreeLog" }))
		{
			TreeLog component = gameObject.GetComponent<TreeLog>();
			targetDamageModifiers = component.m_damages;
		}
		else if (ExposedGameObjectExtension.HasAnyComponent(gameObject, new string[1] { "TreeBase" }))
		{
			TreeBase component2 = gameObject.GetComponent<TreeBase>();
			targetDamageModifiers = component2.m_damageModifiers;
		}
		else if (ExposedGameObjectExtension.HasAnyComponent(gameObject, new string[2] { "Character", "Humanoid" }))
		{
			Character component3 = gameObject.GetComponent<Character>();
			if (Object.op_Implicit((Object)(object)component3))
			{
				targetDamageModifiers = component3.m_damageModifiers;
			}
			else
			{
				Humanoid component4 = gameObject.GetComponent<Humanoid>();
				if (Object.op_Implicit((Object)(object)component4))
				{
					targetDamageModifiers = ((Character)component4).m_damageModifiers;
				}
			}
		}
		else if (ExposedGameObjectExtension.HasAnyComponent(gameObject, new string[1] { "MineRock" }))
		{
			MineRock component5 = gameObject.GetComponent<MineRock>();
			targetDamageModifiers = component5.m_damageModifiers;
		}
		else if (ExposedGameObjectExtension.HasAnyComponent(gameObject, new string[1] { "MineRock5" }))
		{
			MineRock5 component6 = gameObject.GetComponent<MineRock5>();
			targetDamageModifiers = component6.m_damageModifiers;
		}
		else
		{
			if (!ExposedGameObjectExtension.HasAnyComponent(gameObject, new string[1] { "Destructible" }))
			{
				targetDamageModifiers = default(DamageModifiers);
				return;
			}
			Destructible component7 = gameObject.GetComponent<Destructible>();
			targetDamageModifiers = component7.m_damages;
		}
		if (Object.op_Implicit((Object)(object)humanoid_PlayerNPC))
		{
			ItemData bestHarvestingTool = GetBestHarvestingTool(((Humanoid)humanoid_PlayerNPC).m_inventory.m_inventory, targetDamageModifiers);
			if (bestHarvestingTool != null)
			{
				LogInfo("Equipping best weapon: " + bestHarvestingTool.m_shared.m_name + " for " + CleanKey(((Object)gameObject).name));
				useWeapon = bestHarvestingTool;
				((Humanoid)humanoid_PlayerNPC).EquipItem(bestHarvestingTool, true);
			}
		}
	}

	public static ItemData GetBestHarvestingTool(List<ItemData> tools, DamageModifiers resourceDamageModifiers)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		return tools.OrderByDescending(CalculateEffectiveDamage).FirstOrDefault();
		static float ApplyDamageModifier(float baseDamage, DamageModifier modifier)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected I4, but got Unknown
			switch ((int)modifier)
			{
			case 0:
				return baseDamage;
			case 1:
				return baseDamage * 0.5f;
			case 2:
				return baseDamage * 1.5f;
			case 3:
			case 4:
				return 0f;
			case 5:
				return baseDamage * 0.25f;
			case 6:
				return baseDamage * 2f;
			default:
				return baseDamage;
			}
		}
		float CalculateEffectiveDamage(ItemData tool)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			DamageTypes damages = tool.m_shared.m_damages;
			float num = 0f;
			if ((int)tool.m_shared.m_skillType == 8)
			{
				return 0f;
			}
			num += ApplyDamageModifier(damages.m_blunt, resourceDamageModifiers.m_blunt);
			num += ApplyDamageModifier(damages.m_slash, resourceDamageModifiers.m_slash);
			num += ApplyDamageModifier(damages.m_pierce, resourceDamageModifiers.m_pierce);
			num += ApplyDamageModifier(damages.m_chop, resourceDamageModifiers.m_chop);
			num += ApplyDamageModifier(damages.m_pickaxe, resourceDamageModifiers.m_pickaxe);
			num += ApplyDamageModifier(damages.m_fire, resourceDamageModifiers.m_fire);
			num += ApplyDamageModifier(damages.m_frost, resourceDamageModifiers.m_frost);
			num += ApplyDamageModifier(damages.m_lightning, resourceDamageModifiers.m_lightning);
			num += ApplyDamageModifier(damages.m_poison, resourceDamageModifiers.m_poison);
			return num + ApplyDamageModifier(damages.m_spirit, resourceDamageModifiers.m_spirit);
		}
	}

	private float GetDamageModifierValue(DamageModifier modifier)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Expected I4, but got Unknown
		return (int)modifier switch
		{
			0 => 1f, 
			1 => 0.5f, 
			2 => 1.5f, 
			3 => 0f, 
			4 => 1f, 
			5 => 0.25f, 
			6 => 2f, 
			_ => 1f, 
		};
	}

	private float CalculateWeaponEffectiveness(ItemData weapon, DamageModifiers resourceModifiers)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: 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_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: 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_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_014a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0194: Unknown result type (might be due to invalid IL or missing references)
		//IL_019b: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		float num = 0f;
		DamageTypes damages = weapon.m_shared.m_damages;
		if (damages.m_blunt > 0f)
		{
			num += damages.m_blunt * GetDamageModifierValue(resourceModifiers.m_blunt);
		}
		if (damages.m_slash > 0f)
		{
			num += damages.m_slash * GetDamageModifierValue(resourceModifiers.m_slash);
		}
		if (damages.m_pierce > 0f)
		{
			num += damages.m_pierce * GetDamageModifierValue(resourceModifiers.m_pierce);
		}
		if (damages.m_chop > 0f)
		{
			num += damages.m_chop * GetDamageModifierValue(resourceModifiers.m_chop);
		}
		if (damages.m_pickaxe > 0f)
		{
			num += damages.m_pickaxe * GetDamageModifierValue(resourceModifiers.m_pickaxe);
		}
		if (damages.m_fire > 0f)
		{
			num += damages.m_fire * GetDamageModifierValue(resourceModifiers.m_fire);
		}
		if (damages.m_frost > 0f)
		{
			num += damages.m_frost * GetDamageModifierValue(resourceModifiers.m_frost);
		}
		if (damages.m_lightning > 0f)
		{
			num += damages.m_lightning * GetDamageModifierValue(resourceModifiers.m_lightning);
		}
		if (damages.m_poison > 0f)
		{
			num += damages.m_poison * GetDamageModifierValue(resourceModifiers.m_poison);
		}
		if (damages.m_spirit > 0f)
		{
			num += damages.m_spirit * GetDamageModifierValue(resourceModifiers.m_spirit);
		}
		return num;
	}

	private float CalculateEfficiency(string name, string category, int minAmount, int maxAmount, float health, float weaponEffectiveness, float distance)
	{
		float num = (float)(minAmount + maxAmount) / 2f;
		if (weaponEffectiveness <= 5f)
		{
			if (category == "ItemDrop" || category == "Pickable")
			{
				return num / (1f + distance / 50f);
			}
			return 0f;
		}
		float num2 = num / (health / weaponEffectiveness);
		if (category == "ItemDrop" || category == "Pickable")
		{
			num2 = 1f;
		}
		else
		{
			switch (category)
			{
			case "DropOnDestroyed":
				num2 *= num;
				break;
			default:
				if (!(category == "Destructible"))
				{
					if (category == "CharacterDrop")
					{
						num2 *= 0.5f;
					}
					break;
				}
				goto case "TreeLog";
			case "TreeLog":
			case "TreeBase":
			case "MineRock":
			case "MineRock5":
				if (weaponEffectiveness <= 5f)
				{
					return 0f;
				}
				num2 *= num;
				if (name.ToLower().Contains("log"))
				{
					num2 *= 1.1f;
				}
				if (name.ToLower().Contains("half"))
				{
					num2 *= 1.1f;
				}
				break;
			}
		}
		float num3 = 1f / (1f + distance / 10f);
		return num2 * num3;
	}

	private List<(string Category, string Name, float Efficiency, float Distance, int Depth)> FindResourceSourcesRecursive(string resource, ItemData weapon, int depth = 0, int maxDepth = 3)
	{
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		List<(string, string, float, float, int)> list = new List<(string, string, float, float, int)>();
		if (!resourceDatabase.ContainsKey(resource) || depth > maxDepth)
		{
			return list;
		}
		GetNearbyResources(((Component)Player.m_localPlayer).gameObject);
		foreach (KeyValuePair<string, List<Resource>> item2 in resourceDatabase[resource])
		{
			foreach (Resource item3 in item2.Value)
			{
				if (nearbyResourcesDistance.TryGetValue(item3.Name, out var value))
				{
					float weaponEffectiveness = CalculateWeaponEffectiveness(weapon, item3.DamageModifiers);
					float item = CalculateEfficiency(item3.Name, item2.Key, item3.MinAmount, item3.MaxAmount, item3.Health, weaponEffectiveness, value);
					list.Add((item2.Key, item3.Name, item, value, depth));
				}
			}
		}
		return list;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
	private static bool MonsterAI_CustomFixedUpdate_Prefix(MonsterAI __instance)
	{
		//IL_03e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_0410: Unknown result type (might be due to invalid IL or missing references)
		//IL_041a: Unknown result type (might be due to invalid IL or missing references)
		//IL_04bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_050c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0779: Unknown result type (might be due to invalid IL or missing references)
		//IL_0861: Unknown result type (might be due to invalid IL or missing references)
		//IL_05a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_05ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_0606: Unknown result type (might be due to invalid IL or missing references)
		//IL_06b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_06be: Unknown result type (might be due to invalid IL or missing references)
		//IL_06f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0704: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)__instance))
		{
			return true;
		}
		if (!((Object)__instance).name.Contains("HumanoidNPC"))
		{
			return true;
		}
		HumanoidNPC component = ((Component)__instance).gameObject.GetComponent<HumanoidNPC>();
		if (instance.NPCCurrentMode != 0 && enemyList.Count > 0)
		{
			((BaseAI)__instance).m_viewRange = 80f;
			if ((Object)(object)__instance.m_targetCreature == (Object)null || !enemyList.Contains(__instance.m_targetCreature))
			{
				if ((Object)(object)__instance.m_targetCreature == (Object)null)
				{
					LogError("__instance.m_targetCreature == null");
				}
				else if (!enemyList.Contains(__instance.m_targetCreature))
				{
					LogError($"{enemyList.Count} !enemyList.Contains(__instance.m_targetCreature.gameObject)");
				}
				_ = from go in enemyList
					where (Object)(object)go != (Object)null
					orderby VectorExtensions.DistanceTo(((Component)go).transform.position, ((Component)__instance).transform.position)
					select go;
				Character[] array = enemyList.ToArray();
				foreach (Character val in array)
				{
					if (Object.op_Implicit((Object)(object)val))
					{
						Character val2 = val;
						if ((Object)(object)val2 != (Object)null)
						{
							__instance.m_targetCreature = null;
							__instance.SetTarget(val2);
							__instance.m_updateTargetTimer = 1000000f;
							LogError("New enemy in defensive mode: " + ((Object)val2).name);
							((BaseAI)__instance).m_alerted = false;
							((BaseAI)__instance).m_aggravatable = true;
							((BaseAI)__instance).SetHuntPlayer(true);
							return true;
						}
						LogError("Defensive mode enemy from enemyList wasn't a character");
					}
				}
				if (!Object.op_Implicit((Object)(object)__instance.m_targetCreature) && enemyList.Count > 0)
				{
					enemyList.Clear();
					LogError("enemy list cleared");
				}
			}
		}
		NPCCommand nextCommand = instance.commandManager.GetNextCommand();
		if (instance.currentcommand == null || (instance.currentcommand != nextCommand && nextCommand != null))
		{
			instance.currentcommand = nextCommand;
			if (nextCommand is HarvestAction)
			{
				HarvestAction harvestAction = (HarvestAction)nextCommand;
				instance.Harvesting_Start(harvestAction.ResourceName, "");
			}
			if (nextCommand is PatrolAction)
			{
				PatrolAction patrolAction = (PatrolAction)nextCommand;
				instance.Patrol_Start("");
			}
			if (nextCommand is AttackAction)
			{
				AttackAction attackAction = (AttackAction)nextCommand;
				instance.Combat_StartAttacking(attackAction.TargetName, "");
			}
			if (nextCommand is FollowAction)
			{
				FollowAction followAction = (FollowAction)nextCommand;
				instance.Follow_Start(((Component)Player.m_localPlayer).gameObject, "");
			}
		}
		if (nextCommand == null && instance.commandManager.GetCommandsCount() == 0)
		{
			FollowAction command = new FollowAction();
			instance.commandManager.AddCommand(command);
		}
		if (instance.NPCCurrentCommand == NPCCommand.CommandType.PatrolArea && instance.patrol_position != Vector3.zero)
		{
			float num = VectorExtensions.DistanceTo(((Component)__instance).transform.position, instance.patrol_position);
			if (num > instance.chaseUntilPatrolRadiusDistance && !instance.MovementLock)
			{
				SetMonsterAIAggravated(__instance, Aggravated: false);
				instance.MovementLock = true;
				LogInfo($"{((Character)component).m_name} went too far ({instance.chaseUntilPatrolRadiusDistance}m away) from patrol position, heading back now!");
			}
			else if (num < instance.patrol_radius - 3f)
			{
				instance.MovementLock = false;
				__instance.m_lastKnownTargetPos = instance.patrol_position;
			}
			else if (num < instance.patrol_radius)
			{
				((BaseAI)__instance).m_aggravatable = true;
			}
			if (instance.MovementLock)
			{
				((BaseAI)__instance).MoveTo(Time.deltaTime, instance.patrol_position, 0f, false);
				return false;
			}
			GameObject follow = __instance.m_follow;
			if ((Object)(object)follow != (Object)null && (ExposedGameObjectExtension.HasAnyComponent(follow, new string[1] { "Character" }) || ExposedGameObjectExtension.HasAnyComponent(follow, new string[1] { "Humanoid" })))
			{
				return true;
			}
			if (instance.patrol_harvest)
			{
				if ((Object)(object)follow == (Object)null || VectorExtensions.DistanceTo(follow.transform.position, instance.patrol_position) > instance.chaseUntilPatrolRadiusDistance || (!ExposedGameObjectExtension.HasAnyComponent(follow, new string[1] { "Pickable" }) && !ExposedGameObjectExtension.HasAnyComponent(follow, new string[1] { "ItemDrop" })))
				{
					List<Pickable> list = SphereSearchForGameObjectsWithComponent<Pickable>(instance.patrol_position, instance.chaseUntilPatrolRadiusDistance - 2f);
					if (list.Count == 0)
					{
						LogMessage(((Character)component).m_name + " has picked up all dropped items around the patrolling area. Only keeping guard now!");
						instance.patrol_harvest = false;
						return true;
					}
					foreach (Pickable item in list)
					{
						if ((Object)(object)item == (Object)null)
						{
							LogMessage(((Character)component).m_name + " has picked up all dropped items around the patrolling area. Only keeping guard now!");
							instance.patrol_harvest = false;
							return true;
						}
						if (VectorExtensions.DistanceTo(((Component)item).transform.position, instance.patrol_position) < instance.chaseUntilPatrolRadiusDistance)
						{
							LogMessage($"{((Character)component).m_name} is going to pickup {((Object)item).name} in patrol area, distance: {VectorExtensions.DistanceTo(((Component)item).transform.position, ((Component)__instance).transform.position)}");
							__instance.SetFollowTarget(((Component)item).gameObject);
							return true;
						}
						LogInfo("Closest pickable's distance is too far!");
					}
				}
				return true;
			}
			((BaseAI)__instance).RandomMovementArroundPoint(Time.deltaTime, instance.patrol_position, 7f, false);
			return false;
		}
		if (instance.NPCCurrentCommand == NPCCommand.CommandType.HarvestResource && enemyList.Count == 0)
		{
			if (((Object)(object)__instance.m_follow == (Object)null && (Object)(object)__instance.m_targetCreature == (Object)null) || !ResourceNodesOneArray.Contains(CleanKey(((Object)__instance.m_follow).name)) || (Object)(object)__instance.m_follow == (Object)(object)Player.m_localPlayer)
			{
				if (Time.time - closestItemDropsLas

plugins\ValheimAIModLoader.dll

Decompiled 2 weeks ago
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ValheimAIModLoader")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ValheimAIModLoader")]
[assembly: AssemblyCopyright("Copyright ©  2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d0f5469c-3db9-4874-86e8-35b484bf63d1")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace ValheimAIMod
{
	[BepInPlugin("egoai.thrallmodloader", "ego.ai Thrall Mod Loader", "1.0.0")]
	[BepInProcess("valheim.exe")]
	public class ValheimAIModLoader : BaseUnityPlugin
	{
		public class DespawnAllCommand : ConsoleCommand
		{
			public override string Name => "despawn_all";

			public override string Help => "Despawn all __INPUT_TEXT__ game objects";

			public override void Run(string[] args)
			{
				if (args.Length == 0)
				{
					instance.DespawnPrefabInstances("HumanoidNPC");
				}
				else
				{
					instance.DespawnPrefabInstances(args[0]);
				}
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ZNetScene_Awake_Patch
		{
			public static void Prefix(ZNetScene __instance)
			{
				if (!((Object)(object)__instance == (Object)null))
				{
				}
			}
		}

		private static ValheimAIModLoader instance;

		private readonly Harmony harmony = new Harmony("egoai.thrallmodloader");

		private static GameObject HumanoidNPCPrefab;

		private void Awake()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			instance = this;
			RegisterConsoleCommands();
			AssetBundle assetBundleFromResources = GetAssetBundleFromResources("scriptnpc");
			HumanoidNPCPrefab = assetBundleFromResources.LoadAsset<GameObject>("Assets/CustomAssets/HumanoidNPC.prefab");
			PrefabManager.Instance.AddPrefab(new CustomPrefab(HumanoidNPCPrefab, true));
			if (Object.op_Implicit((Object)(object)HumanoidNPCPrefab))
			{
				Debug.Log((object)"HumanoidNPCPrefab loaded");
			}
			else
			{
				Debug.LogError((object)"HumanoidNPCPrefab not loaded");
			}
			assetBundleFromResources.Unload(false);
			harmony.PatchAll();
		}

		private void OnDestroy()
		{
			harmony.UnpatchSelf();
		}

		public static AssetBundle GetAssetBundleFromResources(string fileName)
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string name = executingAssembly.GetManifestResourceNames().Single((string str) => str.EndsWith(fileName));
			using Stream stream = executingAssembly.GetManifestResourceStream(name);
			return AssetBundle.LoadFromStream(stream);
		}

		private void RegisterConsoleCommands()
		{
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new DespawnAllCommand());
		}

		public void DespawnPrefabInstances(string prefabName)
		{
			List<GameObject> list = new List<GameObject>();
			ZNetView[] array = Object.FindObjectsOfType<ZNetView>();
			foreach (ZNetView val in array)
			{
				if (((Object)((Component)val).gameObject).name.Contains(prefabName))
				{
					list.Add(((Component)val).gameObject);
				}
			}
			foreach (GameObject item in list)
			{
				ZNetView component = item.GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null)
				{
					component.Destroy();
				}
			}
			Console.instance.Print($"Despawned {list.Count} instances of prefab '{prefabName}'");
		}
	}
}
namespace ValheimAIModLoader
{
	public class HumanoidNPC : Humanoid
	{
		[Header("HumanoidNPC")]
		public float m_staminaRegen = 5f;

		public float m_staminaRegenTimeMultiplier = 1f;

		public float m_staminaRegenDelay = 1f;

		public float m_runStaminaDrain = 10f;

		public float m_sneakStaminaDrain = 5f;

		public float m_swimStaminaDrainMinSkill = 5f;

		public float m_swimStaminaDrainMaxSkill = 2f;

		public float m_dodgeStaminaUsage = 10f;

		public float m_weightStaminaFactor = 0.1f;

		public float m_eiterRegen = 5f;

		public float m_eitrRegenDelay = 1f;

		public float m_autoPickupRange = 2f;

		public float m_maxCarryWeight = 300f;

		public float m_encumberedStaminaDrain = 10f;

		public float m_hardDeathCooldown = 10f;

		public float m_baseCameraShake = 4f;

		public float m_placeDelay = 0.4f;

		public float m_removeDelay = 0.25f;

		public float m_baseHP = 25f;

		public float m_baseStamina = 75f;

		private float m_timeSinceDeath = 999999f;

		private float m_nearFireTimer;

		private bool m_underRoof = true;

		private bool m_safeInHome;

		private float m_timeSinceSensed;

		private float m_coverPercentage;

		private int m_baseValue;

		private int m_baseValueOld = -1;

		public readonly List<Food> m_foods = new List<Food>();

		private float m_foodUpdateTimer;

		private float m_foodRegenTimer;

		public float m_stamina = 100f;

		public float m_maxStamina = 100f;

		public float m_staminaRegenTimer;

		public float m_staminaLastBreakTime = 0f;

		public float StaminaExhaustedMinimumBreakTime = 2f;

		public float MinimumStaminaToRun = 5f;

		private float m_eitr;

		private float m_maxEitr;

		private float m_eitrRegenTimer;

		private static bool m_enableAutoPickup = true;

		private int m_autoPickupMask;

		public bool m_crouchToggled;

		private static readonly int s_crouching = ZSyncAnimation.GetHash("crouching");

		private static readonly int s_animatorTagCrouch = ZSyncAnimation.GetHash("crouch");

		public Vector3 LastPosition;

		public float LastMovedAtTime;

		public Vector3 patrol_position;

		public PinData npcPinData;

		public Container inventoryContainer;

		public override void CustomFixedUpdate(float fixedDeltaTime)
		{
			((Humanoid)this).CustomFixedUpdate(fixedDeltaTime);
			UpdateStats(fixedDeltaTime);
			UpdateCrouch(fixedDeltaTime);
			UpdateLastPosition();
			UpdatePin();
		}

		private void UpdateStats(float dt)
		{
			if ((Object)(object)this == (Object)null || ((Character)this).InIntro() || ((Character)this).IsTeleporting())
			{
				return;
			}
			m_timeSinceDeath += dt;
			bool flag = ((Character)this).IsEncumbered();
			float maxStamina = m_maxStamina;
			float num = 1f;
			if (((Character)this).IsBlocking())
			{
				num *= 0.8f;
			}
			if ((((Character)this).IsSwimming() && !((Character)this).IsOnGround()) || ((Character)this).InAttack() || ((Character)this).InDodge() || ((Character)this).m_wallRunning || flag)
			{
				num = 0f;
			}
			float num2 = (m_staminaRegen + (1f - m_stamina / maxStamina) * m_staminaRegen * m_staminaRegenTimeMultiplier) * num;
			float num3 = 1f;
			((Character)this).m_seman.ModifyStaminaRegen(ref num3);
			num2 *= num3;
			m_staminaRegenTimer -= dt;
			if (m_stamina < maxStamina && m_staminaRegenTimer <= 0f)
			{
				m_stamina = Mathf.Min(maxStamina, m_stamina + num2 * dt * Game.m_staminaRegenRate);
			}
			float maxEitr = ((Character)this).GetMaxEitr();
			float num4 = 1f;
			if (((Character)this).IsBlocking())
			{
				num4 *= 0.8f;
			}
			if (((Character)this).InAttack() || ((Character)this).InDodge())
			{
				num4 = 0f;
			}
			if (flag)
			{
				if (((Vector3)(ref ((Character)this).m_moveDir)).magnitude > 0.1f)
				{
					((Character)this).UseStamina(m_encumberedStaminaDrain * dt, false);
				}
				((Character)this).m_seman.AddStatusEffect(SEMan.s_statusEffectEncumbered, false, 0, 0f);
			}
			UpdateEnvStatusEffects(dt);
		}

		public bool InShelter()
		{
			if (m_coverPercentage >= 0.8f)
			{
				return m_underRoof;
			}
			return false;
		}

		public bool IsSensed()
		{
			return m_timeSinceSensed < 1f;
		}

		private void UpdateEnvStatusEffects(float dt)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Invalid comparison between Unknown and I4
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Invalid comparison between Unknown and I4
			m_nearFireTimer += dt;
			DamageModifiers damageModifiers = ((Character)this).GetDamageModifiers((WeakSpot)null);
			bool flag = m_nearFireTimer < 0.25f;
			bool flag2 = ((Character)this).m_seman.HaveStatusEffect(SEMan.s_statusEffectBurning);
			bool flag3 = InShelter();
			DamageModifier modifier = ((DamageModifiers)(ref damageModifiers)).GetModifier((DamageType)64);
			bool flag4 = EnvMan.IsFreezing();
			bool flag5 = EnvMan.IsCold();
			bool flag6 = EnvMan.IsWet();
			bool flag7 = IsSensed();
			bool flag8 = ((Character)this).m_seman.HaveStatusEffect(SEMan.s_statusEffectWet);
			bool flag9 = ((Character)this).IsSitting();
			bool flag10 = Object.op_Implicit((Object)(object)EffectArea.IsPointInsideArea(((Component)this).transform.position, (Type)64, 1f));
			bool flag11 = ShieldGenerator.IsInsideShield(((Component)this).transform.position);
			bool flag12 = flag4 && !flag && !flag3;
			bool flag13 = (flag5 && !flag) || (flag4 && flag && !flag3) || (flag4 && !flag && flag3);
			if ((int)modifier == 1 || (int)modifier == 5 || flag10)
			{
				flag12 = false;
				flag13 = false;
			}
			if (flag6 && !m_underRoof && !flag11)
			{
				((Character)this).m_seman.AddStatusEffect(SEMan.s_statusEffectWet, true, 0, 0f);
			}
			if (flag3)
			{
				((Character)this).m_seman.AddStatusEffect(SEMan.s_statusEffectShelter, false, 0, 0f);
			}
			else
			{
				((Character)this).m_seman.RemoveStatusEffect(SEMan.s_statusEffectShelter, false);
			}
			if (flag)
			{
				((Character)this).m_seman.AddStatusEffect(SEMan.s_statusEffectCampFire, false, 0, 0f);
			}
			else
			{
				((Character)this).m_seman.RemoveStatusEffect(SEMan.s_statusEffectCampFire, false);
			}
			bool flag14 = !flag7 && (flag9 || flag3) && !flag13 && !flag12 && (!flag8 || flag10) && !flag2 && flag;
			if (flag14)
			{
				((Character)this).m_seman.AddStatusEffect(SEMan.s_statusEffectResting, false, 0, 0f);
			}
			else
			{
				((Character)this).m_seman.RemoveStatusEffect(SEMan.s_statusEffectResting, false);
			}
			m_safeInHome = flag14 && flag3;
			if (flag12)
			{
				if (!((Character)this).m_seman.RemoveStatusEffect(SEMan.s_statusEffectCold, true))
				{
					((Character)this).m_seman.AddStatusEffect(SEMan.s_statusEffectFreezing, false, 0, 0f);
				}
			}
			else if (flag13)
			{
				if (!((Character)this).m_seman.RemoveStatusEffect(SEMan.s_statusEffectFreezing, true) && !Object.op_Implicit((Object)(object)((Character)this).m_seman.AddStatusEffect(SEMan.s_statusEffectCold, false, 0, 0f)))
				{
				}
			}
			else
			{
				((Character)this).m_seman.RemoveStatusEffect(SEMan.s_statusEffectCold, false);
				((Character)this).m_seman.RemoveStatusEffect(SEMan.s_statusEffectFreezing, false);
			}
		}

		public void UpdatePin()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (npcPinData != null)
			{
				Minimap.instance.RemovePin(npcPinData);
			}
			npcPinData = Minimap.instance.AddPin(((Component)this).transform.position, (PinType)10, ((Character)this).m_name, false, false, 9990L, "NPC");
		}

		public void UpdateLastPosition()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (VectorExtensions.DistanceTo(((Component)this).transform.position, LastPosition) > 0.15f)
			{
				LastPosition = ((Component)this).transform.position;
				LastMovedAtTime = Time.time;
			}
		}

		public override void Awake()
		{
			((Humanoid)this).Awake();
			m_autoPickupMask = LayerMask.GetMask(new string[1] { "item" });
			((Character)this).m_nview = ((Component)this).GetComponent<ZNetView>();
			if ((Object)(object)((Character)this).m_nview == (Object)null)
			{
				Debug.LogError((object)"PersistentNPC: Missing ZNetView component");
				return;
			}
			Debug.Log((object)$"m_autoPickupMask: {m_autoPickupMask}");
			((Character)this).m_nview.m_persistent = true;
		}

		public bool HasEnoughResource(string resourceName, int requiredAmount)
		{
			Inventory inventory = ((Humanoid)this).GetInventory();
			if (inventory == null)
			{
				Debug.LogError((object)"Player inventory not found!");
				return false;
			}
			List<ItemData> source = (from item in inventory.GetAllItems()
				where ((Object)item.m_dropPrefab).name.ToLower() == resourceName.ToLower()
				select item).ToList();
			int num = source.Sum((ItemData item) => item.m_stack);
			return num >= requiredAmount;
		}

		public override bool IsCrouching()
		{
			return m_crouchToggled;
		}

		private void UpdateCrouch(float dt)
		{
			if (m_crouchToggled)
			{
				if (!((Character)this).HaveStamina(0f) || ((Character)this).IsSwimming() || ((Character)this).InBed() || ((Character)this).InPlaceMode() || ((Character)this).m_run || ((Character)this).IsBlocking() || ((Character)this).IsFlying())
				{
					((Character)this).SetCrouch(false);
				}
				bool flag = ((Character)this).InAttack() || ((Character)this).IsDrawingBow();
				((Character)this).m_zanim.SetBool(s_crouching, m_crouchToggled && !flag);
			}
			else
			{
				((Character)this).m_zanim.SetBool(s_crouching, false);
			}
		}

		public override void SetCrouch(bool crouch)
		{
			m_crouchToggled = crouch;
		}

		public override bool IsEncumbered()
		{
			return false;
		}

		public float GetMaxCarryWeight()
		{
			float maxCarryWeight = m_maxCarryWeight;
			((Character)this).m_seman.ModifyMaxCarryWeight(maxCarryWeight, ref maxCarryWeight);
			return maxCarryWeight;
		}

		public override bool CheckRun(Vector3 moveDir, float dt)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			if (!((Humanoid)this).CheckRun(moveDir, dt))
			{
				return false;
			}
			bool flag = ((Character)this).HaveStamina(0f);
			float num = 1f;
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				num = Player.m_localPlayer.m_skills.GetSkillFactor((SkillType)102);
			}
			float num2 = Mathf.Lerp(1f, 0.5f, num);
			float num3 = m_runStaminaDrain * num2;
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				num3 -= num3 * ((Character)Player.m_localPlayer).GetEquipmentMovementModifier();
				num3 += num3 * ((Character)Player.m_localPlayer).GetEquipmentRunStaminaModifier();
			}
			if (m_stamina > MinimumStaminaToRun)
			{
				m_stamina -= dt * num3 * Game.m_moveStaminaRate;
				return true;
			}
			return false;
		}

		public override void SetupVisEquipment(VisEquipment visEq, bool isRagdoll)
		{
			if (!isRagdoll)
			{
				visEq.SetLeftItem((base.m_leftItem != null) ? ((Object)base.m_leftItem.m_dropPrefab).name : "", (base.m_leftItem != null) ? base.m_leftItem.m_variant : 0);
				visEq.SetRightItem((base.m_rightItem != null) ? ((Object)base.m_rightItem.m_dropPrefab).name : "");
				visEq.SetLeftBackItem((base.m_hiddenLeftItem != null) ? ((Object)base.m_hiddenLeftItem.m_dropPrefab).name : "", (base.m_hiddenLeftItem != null) ? base.m_hiddenLeftItem.m_variant : 0);
				visEq.SetRightBackItem((base.m_hiddenRightItem != null) ? ((Object)base.m_hiddenRightItem.m_dropPrefab).name : "");
			}
			visEq.SetChestItem((base.m_chestItem != null) ? ((Object)base.m_chestItem.m_dropPrefab).name : "");
			visEq.SetLegItem((base.m_legItem != null) ? ((Object)base.m_legItem.m_dropPrefab).name : "");
			visEq.SetHelmetItem((base.m_helmetItem != null) ? ((Object)base.m_helmetItem.m_dropPrefab).name : "");
			visEq.SetShoulderItem((base.m_shoulderItem != null) ? ((Object)base.m_shoulderItem.m_dropPrefab).name : "", (base.m_shoulderItem != null) ? base.m_shoulderItem.m_variant : 0);
			visEq.SetUtilityItem((base.m_utilityItem != null) ? ((Object)base.m_utilityItem.m_dropPrefab).name : "");
			visEq.SetBeardItem(base.m_beardItem);
			visEq.SetHairItem(base.m_hairItem);
		}

		public override void ApplyArmorDamageMods(ref DamageModifiers mods)
		{
			if (base.m_chestItem != null)
			{
				((DamageModifiers)(ref mods)).Apply(base.m_chestItem.m_shared.m_damageModifiers);
			}
			if (base.m_legItem != null)
			{
				((DamageModifiers)(ref mods)).Apply(base.m_legItem.m_shared.m_damageModifiers);
			}
			if (base.m_helmetItem != null)
			{
				((DamageModifiers)(ref mods)).Apply(base.m_helmetItem.m_shared.m_damageModifiers);
			}
			if (base.m_shoulderItem != null)
			{
				((DamageModifiers)(ref mods)).Apply(base.m_shoulderItem.m_shared.m_damageModifiers);
			}
		}

		public override float GetBodyArmor()
		{
			float num = 0f;
			if (base.m_chestItem != null)
			{
				num += base.m_chestItem.GetArmor();
			}
			if (base.m_legItem != null)
			{
				num += base.m_legItem.GetArmor();
			}
			if (base.m_helmetItem != null)
			{
				num += base.m_helmetItem.GetArmor();
			}
			if (base.m_shoulderItem != null)
			{
				num += base.m_shoulderItem.GetArmor();
			}
			return num;
		}

		public override void DamageArmorDurability(HitData hit)
		{
			List<ItemData> list = new List<ItemData>();
			if (base.m_chestItem != null)
			{
				list.Add(base.m_chestItem);
			}
			if (base.m_legItem != null)
			{
				list.Add(base.m_legItem);
			}
			if (base.m_helmetItem != null)
			{
				list.Add(base.m_helmetItem);
			}
			if (base.m_shoulderItem != null)
			{
				list.Add(base.m_shoulderItem);
			}
			if (list.Count != 0)
			{
				float num = hit.GetTotalPhysicalDamage() + hit.GetTotalElementalDamage();
				if (!(num <= 0f))
				{
					int index = Random.Range(0, list.Count);
					ItemData val = list[index];
					val.m_durability = Mathf.Max(0f, val.m_durability - num);
				}
			}
		}
	}
	public class NPCScript : MonoBehaviour
	{
		protected Humanoid m_humanoid;

		[Header("NPCScript")]
		public float m_maxPlaceDistance = 5f;

		public float m_maxInteractDistance = 5f;

		public float m_scrollSens = 4f;

		public float m_staminaRegen = 5f;

		public float m_staminaRegenTimeMultiplier = 1f;

		public float m_staminaRegenDelay = 1f;

		public float m_runStaminaDrain = 10f;

		public float m_sneakStaminaDrain = 5f;

		public float m_swimStaminaDrainMinSkill = 5f;

		public float m_swimStaminaDrainMaxSkill = 2f;

		public float m_dodgeStaminaUsage = 10f;

		public float m_weightStaminaFactor = 0.1f;

		public float m_eiterRegen = 5f;

		public float m_eitrRegenDelay = 1f;

		public float m_autoPickupRange = 2f;

		public float m_maxCarryWeight = 300f;

		public float m_encumberedStaminaDrain = 10f;

		public float m_hardDeathCooldown = 10f;

		public float m_baseCameraShake = 4f;

		public float m_placeDelay = 0.4f;

		public float m_removeDelay = 0.25f;

		public float m_baseHP = 25f;

		public float m_baseStamina = 75f;

		private float m_timeSinceDeath = 999999f;

		private float m_nearFireTimer;

		private bool m_underRoof = true;

		private bool m_safeInHome;

		private float m_timeSinceSensed;

		private float m_coverPercentage;

		private int m_baseValue;

		private int m_baseValueOld = -1;

		private readonly List<Food> m_foods = new List<Food>();

		private float m_foodUpdateTimer;

		private float m_foodRegenTimer;

		public float m_staminaLastBreakTime;

		public float m_stamina = 100f;

		public float m_maxStamina = 100f;

		private float m_staminaRegenTimer;

		private float m_eitr;

		private float m_maxEitr;

		private float m_eitrRegenTimer;

		private float deltaAdd = 0f;

		private void Start()
		{
			m_humanoid = ((Component)this).GetComponent<Humanoid>();
			Debug.Log((object)("NPCScript started for " + ((Character)m_humanoid).GetHoverName()));
		}

		private void Update()
		{
			UpdateStats(Time.deltaTime);
			if (deltaAdd > 1f)
			{
				deltaAdd = 0f;
			}
			else
			{
				deltaAdd += Time.deltaTime;
			}
		}

		private void UpdateStats(float dt)
		{
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)m_humanoid == (Object)null || ((Character)m_humanoid).InIntro() || ((Character)m_humanoid).IsTeleporting())
			{
				return;
			}
			m_timeSinceDeath += dt;
			UpdateModifiers();
			UpdateFood(dt, forceUpdate: false);
			bool flag = ((Character)m_humanoid).IsEncumbered();
			float maxStamina = m_maxStamina;
			float num = 1f;
			if (((Character)m_humanoid).IsBlocking())
			{
				num *= 0.8f;
			}
			if ((((Character)m_humanoid).IsSwimming() && !((Character)m_humanoid).IsOnGround()) || ((Character)m_humanoid).InAttack() || ((Character)m_humanoid).InDodge() || ((Character)m_humanoid).m_wallRunning || flag)
			{
				num = 0f;
			}
			float num2 = (m_staminaRegen + (1f - m_stamina / maxStamina) * m_staminaRegen * m_staminaRegenTimeMultiplier) * num;
			float num3 = 1f;
			((Character)m_humanoid).m_seman.ModifyStaminaRegen(ref num3);
			num2 *= num3;
			m_staminaRegenTimer -= dt;
			if (m_stamina < maxStamina && m_staminaRegenTimer <= 0f)
			{
				m_stamina = Mathf.Min(maxStamina, m_stamina + num2 * dt * Game.m_staminaRegenRate);
			}
			float maxEitr = ((Character)m_humanoid).GetMaxEitr();
			float num4 = 1f;
			if (((Character)m_humanoid).IsBlocking())
			{
				num4 *= 0.8f;
			}
			if (((Character)m_humanoid).InAttack() || ((Character)m_humanoid).InDodge())
			{
				num4 = 0f;
			}
			if (flag)
			{
				if (((Vector3)(ref ((Character)m_humanoid).m_moveDir)).magnitude > 0.1f)
				{
					UseStamina(m_encumberedStaminaDrain * dt);
				}
				((Character)m_humanoid).m_seman.AddStatusEffect(SEMan.s_statusEffectEncumbered, false, 0, 0f);
			}
			else if (((Character)m_humanoid).CheckRun(((Character)m_humanoid).m_moveDir, dt))
			{
				UseStamina(m_runStaminaDrain * dt);
			}
			else
			{
				((Character)m_humanoid).m_seman.RemoveStatusEffect(SEMan.s_statusEffectEncumbered, false);
			}
			if (!HardDeath())
			{
				((Character)m_humanoid).m_seman.AddStatusEffect(SEMan.s_statusEffectSoftDeath, false, 0, 0f);
			}
			else
			{
				((Character)m_humanoid).m_seman.RemoveStatusEffect(SEMan.s_statusEffectSoftDeath, false);
			}
			UpdateEnvStatusEffects(dt);
		}

		private void UpdateEnvStatusEffects(float dt)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Invalid comparison between Unknown and I4
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Invalid comparison between Unknown and I4
			m_nearFireTimer += dt;
			DamageModifiers damageModifiers = ((Character)m_humanoid).GetDamageModifiers((WeakSpot)null);
			bool flag = m_nearFireTimer < 0.25f;
			bool flag2 = ((Character)m_humanoid).m_seman.HaveStatusEffect(SEMan.s_statusEffectBurning);
			bool flag3 = InShelter();
			DamageModifier modifier = ((DamageModifiers)(ref damageModifiers)).GetModifier((DamageType)64);
			bool flag4 = EnvMan.IsFreezing();
			bool flag5 = EnvMan.IsCold();
			bool flag6 = EnvMan.IsWet();
			bool flag7 = IsSensed();
			bool flag8 = ((Character)m_humanoid).m_seman.HaveStatusEffect(SEMan.s_statusEffectWet);
			bool flag9 = ((Character)m_humanoid).IsSitting();
			bool flag10 = Object.op_Implicit((Object)(object)EffectArea.IsPointInsideArea(((Component)this).transform.position, (Type)64, 1f));
			bool flag11 = ShieldGenerator.IsInsideShield(((Component)this).transform.position);
			bool flag12 = flag4 && !flag && !flag3;
			bool flag13 = (flag5 && !flag) || (flag4 && flag && !flag3) || (flag4 && !flag && flag3);
			if ((int)modifier == 1 || (int)modifier == 5 || flag10)
			{
				flag12 = false;
				flag13 = false;
			}
			if (flag6 && !m_underRoof && !flag11)
			{
				((Character)m_humanoid).m_seman.AddStatusEffect(SEMan.s_statusEffectWet, true, 0, 0f);
			}
			if (flag3)
			{
				((Character)m_humanoid).m_seman.AddStatusEffect(SEMan.s_statusEffectShelter, false, 0, 0f);
			}
			else
			{
				((Character)m_humanoid).m_seman.RemoveStatusEffect(SEMan.s_statusEffectShelter, false);
			}
			if (flag)
			{
				((Character)m_humanoid).m_seman.AddStatusEffect(SEMan.s_statusEffectCampFire, false, 0, 0f);
			}
			else
			{
				((Character)m_humanoid).m_seman.RemoveStatusEffect(SEMan.s_statusEffectCampFire, false);
			}
			bool flag14 = !flag7 && (flag9 || flag3) && !flag13 && !flag12 && (!flag8 || flag10) && !flag2 && flag;
			if (flag14)
			{
				((Character)m_humanoid).m_seman.AddStatusEffect(SEMan.s_statusEffectResting, false, 0, 0f);
			}
			else
			{
				((Character)m_humanoid).m_seman.RemoveStatusEffect(SEMan.s_statusEffectResting, false);
			}
			m_safeInHome = flag14 && flag3 && (float)GetBaseValue() >= 1f;
			if (flag12)
			{
				if (!((Character)m_humanoid).m_seman.RemoveStatusEffect(SEMan.s_statusEffectCold, true))
				{
					((Character)m_humanoid).m_seman.AddStatusEffect(SEMan.s_statusEffectFreezing, false, 0, 0f);
				}
			}
			else
			{
				((Character)m_humanoid).m_seman.RemoveStatusEffect(SEMan.s_statusEffectCold, false);
				((Character)m_humanoid).m_seman.RemoveStatusEffect(SEMan.s_statusEffectFreezing, false);
			}
		}

		public bool InShelter()
		{
			if (m_coverPercentage >= 0.8f)
			{
				return m_underRoof;
			}
			return false;
		}

		public float GetStamina()
		{
			return m_stamina;
		}

		public void UseStamina(float v, bool isHomeUsage = false)
		{
			if (v == 0f)
			{
				return;
			}
			v *= Game.m_staminaRate;
			if (isHomeUsage)
			{
				v *= 1f + ((Character)m_humanoid).GetEquipmentHomeItemModifier();
				((Character)m_humanoid).m_seman.ModifyHomeItemStaminaUsage(v, ref v, true);
			}
			if (((Character)m_humanoid).m_nview.IsValid())
			{
				if (((Character)m_humanoid).m_nview.IsOwner())
				{
					RPC_UseStamina(0L, v);
				}
				else
				{
					RPC_UseStamina(0L, v);
				}
			}
		}

		private void RPC_UseStamina(long sender, float v)
		{
			if (v != 0f)
			{
				m_stamina -= v;
				if (m_stamina < 0f)
				{
					m_stamina = 0f;
				}
				m_staminaRegenTimer = m_staminaRegenDelay;
			}
		}

		public bool HaveStamina(float amount = 0f)
		{
			if (((Character)m_humanoid).m_nview.IsValid() && !((Character)m_humanoid).m_nview.IsOwner())
			{
				return ((Character)m_humanoid).m_nview.GetZDO().GetFloat(ZDOVars.s_stamina, m_maxStamina) > amount;
			}
			return m_stamina > amount;
		}

		public int GetBaseValue()
		{
			if (!((Character)m_humanoid).m_nview.IsValid())
			{
				return 0;
			}
			if (((Character)m_humanoid).m_nview.IsOwner())
			{
				return m_baseValue;
			}
			return ((Character)m_humanoid).m_nview.GetZDO().GetInt(ZDOVars.s_baseValue, 0);
		}

		public bool IsSensed()
		{
			return m_timeSinceSensed < 1f;
		}

		private bool HardDeath()
		{
			return m_timeSinceDeath > m_hardDeathCooldown;
		}

		public float GetEquipmentEitrRegenModifier()
		{
			float num = 0f;
			if (m_humanoid.m_chestItem != null)
			{
				num += m_humanoid.m_chestItem.m_shared.m_eitrRegenModifier;
			}
			if (m_humanoid.m_legItem != null)
			{
				num += m_humanoid.m_legItem.m_shared.m_eitrRegenModifier;
			}
			if (m_humanoid.m_helmetItem != null)
			{
				num += m_humanoid.m_helmetItem.m_shared.m_eitrRegenModifier;
			}
			if (m_humanoid.m_shoulderItem != null)
			{
				num += m_humanoid.m_shoulderItem.m_shared.m_eitrRegenModifier;
			}
			if (m_humanoid.m_leftItem != null)
			{
				num += m_humanoid.m_leftItem.m_shared.m_eitrRegenModifier;
			}
			if (m_humanoid.m_rightItem != null)
			{
				num += m_humanoid.m_rightItem.m_shared.m_eitrRegenModifier;
			}
			if (m_humanoid.m_utilityItem != null)
			{
				num += m_humanoid.m_utilityItem.m_shared.m_eitrRegenModifier;
			}
			return num;
		}

		private void UpdateFood(float dt, bool forceUpdate)
		{
		}

		private void UpdateModifiers()
		{
		}
	}
}