Decompiled source of GunnrCompanion v1.0.1

BepInEx/plugins/GunnrCompanion/GunnrCompanion.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
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("GunnrCompanion")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GunnrCompanion")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("9a682378-eb40-46a9-88f7-e0c77670cd0c")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace GunnrCompanion;

[BepInPlugin("ulf.valheim.gunnrcompanion", "Gunnr Companion", "0.1.15-rune-preview")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	[HarmonyPatch]
	private static class GunnrRuneUsePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo[] methods = typeof(Humanoid).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			MethodInfo[] array = methods;
			foreach (MethodInfo method in array)
			{
				if (method != null && method.Name == "UseItem")
				{
					yield return method;
				}
			}
		}

		private static bool Prefix(Humanoid __instance, object[] __args)
		{
			if ((Object)(object)instance == (Object)null || (Object)(object)__instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return true;
			}
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			ItemData item = null;
			if (__args != null)
			{
				foreach (object obj in __args)
				{
					ItemData val = (ItemData)((obj is ItemData) ? obj : null);
					if (val != null)
					{
						item = val;
						break;
					}
				}
			}
			if (!IsGunnrRuneItem(item))
			{
				return true;
			}
			instance.HandleGunnrRuneUse(Player.m_localPlayer);
			return false;
		}
	}

	[HarmonyPatch(typeof(Character), "Damage")]
	private static class CharacterDamagePatch
	{
		private static bool Prefix(Character __instance)
		{
			if (invulnerable != null && invulnerable.Value && IsGunnr(__instance))
			{
				return false;
			}
			return true;
		}
	}

	private const string ModGuid = "ulf.valheim.gunnrcompanion";

	private const string ModName = "Gunnr Companion";

	private const string ModVersion = "0.1.15-rune-preview";

	private const string WolfPrefabName = "Wolf";

	private const string CompanionMarkerKey = "Ulf_Gunnr_Companion";

	private const string CompanionNameKey = "Ulf_Gunnr_Name";

	private const string RunePrefabName = "GunnrsRune";

	private const string RuneDisplayName = "Gunnr's Rune";

	private const string RuneDescription = "Bound by oath, carried by blood. Use to call Gunnr to your side or send her beyond sight.";

	private const string FireAuraChildName = "Ulf_Gunnr_FireAura";

	private const string FrostAuraChildName = "Ulf_Gunnr_FrostAura";

	private const string SpiritAuraChildName = "Ulf_Gunnr_SpiritAura";

	private const string GlowAuraChildName = "Ulf_Gunnr_GlowAura";

	private static ConfigEntry<bool> enableGunnr;

	private static ConfigEntry<string> companionName;

	private static ConfigEntry<int> companionLevel;

	private static ConfigEntry<bool> invulnerable;

	private static ConfigEntry<bool> spawnGunnrNow;

	private static ConfigEntry<bool> summonGunnrNow;

	private static ConfigEntry<bool> spawnIfMissing;

	private static ConfigEntry<bool> manualSpawnOnly;

	private static ConfigEntry<bool> removeDuplicates;

	private static ConfigEntry<float> spawnDistance;

	private static ConfigEntry<float> teleportDistance;

	private static ConfigEntry<bool> enableGunnrRune;

	private static ConfigEntry<bool> giveRuneIfMissing;

	private static ConfigEntry<bool> gunnrStored;

	private static ConfigEntry<float> storedGunnrHealth;

	private static ConfigEntry<float> runeUseCooldown;

	private static ConfigEntry<bool> wolfPupMode;

	private static ConfigEntry<float> wolfPupScale;

	private static ConfigEntry<float> adultWolfScale;

	private static ConfigEntry<bool> avoidWaterSpawn;

	private static ConfigEntry<float> waterHeightBuffer;

	private static ConfigEntry<bool> enableAuras;

	private static ConfigEntry<bool> fireAura;

	private static ConfigEntry<bool> frostAura;

	private static ConfigEntry<bool> spiritAura;

	private static ConfigEntry<bool> glowAura;

	private static ConfigEntry<string> fireAuraPrefab;

	private static ConfigEntry<string> frostAuraPrefab;

	private static ConfigEntry<string> spiritAuraPrefab;

	private static ConfigEntry<float> auraYOffset;

	private static ConfigEntry<float> auraScale;

	private static ConfigEntry<float> glowRange;

	private static ConfigEntry<float> glowIntensity;

	private static ConfigEntry<string> glowColorHex;

	private static ManualLogSource log;

	private static readonly string[] FireAuraCandidates = new string[6] { "vfx_burning", "vfx_Burning", "vfx_fire", "vfx_Fire", "vfx_fire_addfuel", "vfx_FireAddFuel" };

	private static readonly string[] FrostAuraCandidates = new string[6] { "vfx_freeze", "vfx_Freezing", "vfx_frost", "vfx_Frost", "vfx_ColdBall_Hit", "vfx_frost_staff_shield" };

	private static readonly string[] SpiritAuraCandidates = new string[6] { "vfx_StaffShield", "vfx_staff_shield", "vfx_DvergerMage_Mistile_attack", "vfx_eitrgod_explosion", "vfx_GhostDeath", "vfx_WraithDeath" };

	private static Plugin instance;

	private readonly Harmony harmony = new Harmony("ulf.valheim.gunnrcompanion");

	private float nextCompanionCheckTime;

	private float nextRuneUseTime;

	private void Awake()
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Expected O, but got Unknown
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Expected O, but got Unknown
		//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bc: Expected O, but got Unknown
		//IL_0273: Unknown result type (might be due to invalid IL or missing references)
		//IL_027d: Expected O, but got Unknown
		//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02da: Expected O, but got Unknown
		//IL_030d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0317: Expected O, but got Unknown
		//IL_036a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0374: Expected O, but got Unknown
		//IL_04b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_04bd: Expected O, but got Unknown
		//IL_04f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_04fa: Expected O, but got Unknown
		//IL_052d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0537: Expected O, but got Unknown
		//IL_056a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0574: Expected O, but got Unknown
		instance = this;
		log = ((BaseUnityPlugin)this).Logger;
		enableGunnr = ((BaseUnityPlugin)this).Config.Bind<bool>("Gunnr", "Enable Gunnr", true, "Turns the Gunnr companion wolf on or off.");
		companionName = ((BaseUnityPlugin)this).Config.Bind<string>("Gunnr", "Companion Name", "Gunnr", "The custom name used for the companion wolf.");
		companionLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Gunnr", "Wolf Level", 3, new ConfigDescription("Wolf level. 1 = no star, 2 = one star, 3 = two star.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 3), Array.Empty<object>()));
		invulnerable = ((BaseUnityPlugin)this).Config.Bind<bool>("Gunnr", "Invulnerable", true, "If true, Gunnr ignores incoming damage.");
		spawnGunnrNow = ((BaseUnityPlugin)this).Config.Bind<bool>("Gunnr", "Spawn Gunnr Now", false, "Toggle this on to manually spawn Gunnr near you. It automatically turns itself back off after use.");
		summonGunnrNow = ((BaseUnityPlugin)this).Config.Bind<bool>("Gunnr", "Summon Gunnr Now", false, "Toggle this on to move the existing loaded Gunnr to you without spawning a duplicate. It automatically turns itself back off after use.");
		spawnIfMissing = ((BaseUnityPlugin)this).Config.Bind<bool>("Gunnr", "Spawn If Missing", false, "If true, Gunnr is spawned again whenever the mod cannot find her. Leave this off to prevent accidental duplicates.");
		manualSpawnOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Gunnr", "Manual Spawn Only", true, "If true, Gunnr only spawns when Spawn Gunnr Now is toggled. This overrides Spawn If Missing and prevents accidental repeated spawn loops.");
		removeDuplicates = ((BaseUnityPlugin)this).Config.Bind<bool>("Gunnr", "Remove Duplicate Gunnrs", true, "If true, when more than one Gunnr is loaded, the closest one is kept and the extras are removed.");
		spawnDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Gunnr", "Spawn Distance", 2.5f, new ConfigDescription("How far from the player Gunnr appears when spawned or teleported.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
		teleportDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Gunnr", "Teleport Back Distance", 45f, new ConfigDescription("If Gunnr is farther than this from the player, she is moved back near the player. Set to 0 to disable.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), Array.Empty<object>()));
		enableGunnrRune = ((BaseUnityPlugin)this).Config.Bind<bool>("Gunnr's Rune", "Enable Gunnr's Rune", true, "Adds Gunnr's Rune as a reusable inventory item. Use it to summon or stow Gunnr without consuming or equipping the item.");
		giveRuneIfMissing = ((BaseUnityPlugin)this).Config.Bind<bool>("Gunnr's Rune", "Give Rune If Missing", true, "If true, the mod gives the player Gunnr's Rune when it is missing from their inventory.");
		gunnrStored = ((BaseUnityPlugin)this).Config.Bind<bool>("Gunnr's Rune", "Gunnr Is Stored", false, "Internal saved state. True means Gunnr is stored inside Gunnr's Rune and should not exist as a live wolf until summoned.");
		storedGunnrHealth = ((BaseUnityPlugin)this).Config.Bind<float>("Gunnr's Rune", "Stored Gunnr Health", 0f, "Internal saved state. The health Gunnr had when stored. 0 means use default health when released.");
		runeUseCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Gunnr's Rune", "Rune Use Cooldown", 1f, new ConfigDescription("Seconds before Gunnr's Rune can be used again.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
		wolfPupMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Appearance", "Wolf Pup Mode", false, "If true, Gunnr keeps her adult wolf companion behavior but is visually scaled down to look like a wolf pup.");
		wolfPupScale = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "Wolf Pup Scale", 0.55f, new ConfigDescription("Visual scale used when Wolf Pup Mode is on. 0.5 to 0.65 usually looks pup-sized.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 1f), Array.Empty<object>()));
		adultWolfScale = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "Adult Wolf Scale", 1f, new ConfigDescription("Visual scale used when Wolf Pup Mode is off. Leave at 1 for normal wolf size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
		avoidWaterSpawn = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "Avoid Water Spawn Points", true, "If true, the mod tries to spawn or teleport Gunnr only on ground above water. This is water safety only; there is no vehicle logic.");
		waterHeightBuffer = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "Water Height Buffer", 0.3f, new ConfigDescription("Extra height used when deciding whether a spawn point is too close to water.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
		enableAuras = ((BaseUnityPlugin)this).Config.Bind<bool>("Auras", "Enable Aura System", true, "Master switch for all Gunnr aura visuals. These are visual only and do not add damage.");
		fireAura = ((BaseUnityPlugin)this).Config.Bind<bool>("Auras - Fire", "Enable Fire Aura", false, "Adds a fire visual aura to Gunnr. Visual only.");
		fireAuraPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("Auras - Fire", "Fire Aura Prefab", "Auto", "Prefab name for the fire aura. Leave as Auto to try known fire VFX names.");
		frostAura = ((BaseUnityPlugin)this).Config.Bind<bool>("Auras - Frost/Snow", "Enable Frost Snow Aura", false, "Adds a frost/snow visual aura to Gunnr. Visual only.");
		frostAuraPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("Auras - Frost/Snow", "Frost Snow Aura Prefab", "Auto", "Prefab name for the frost/snow aura. Leave as Auto to try known frost VFX names.");
		spiritAura = ((BaseUnityPlugin)this).Config.Bind<bool>("Auras - Spirit", "Enable Spirit Aura", false, "Adds a spirit/eitr-style visual aura to Gunnr. Visual only.");
		spiritAuraPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("Auras - Spirit", "Spirit Aura Prefab", "Auto", "Prefab name for the spirit aura. Leave as Auto to try known spirit/eitr VFX names.");
		glowAura = ((BaseUnityPlugin)this).Config.Bind<bool>("Auras - Glow", "Enable Glow Aura", false, "Adds a point light glow to Gunnr. Visual only.");
		auraYOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Auras - Shared", "Aura Vertical Offset", 0.35f, new ConfigDescription("Moves prefab aura effects up or down on Gunnr.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 3f), Array.Empty<object>()));
		auraScale = ((BaseUnityPlugin)this).Config.Bind<float>("Auras - Shared", "Aura Scale", 1f, new ConfigDescription("Scales prefab aura effects attached to Gunnr.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
		glowRange = ((BaseUnityPlugin)this).Config.Bind<float>("Auras - Glow", "Glow Range", 5f, new ConfigDescription("How far Gunnr's glow light reaches.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 20f), Array.Empty<object>()));
		glowIntensity = ((BaseUnityPlugin)this).Config.Bind<float>("Auras - Glow", "Glow Intensity", 1.25f, new ConfigDescription("Brightness of Gunnr's glow light.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 8f), Array.Empty<object>()));
		glowColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("Auras - Glow", "Glow Color Hex", "#9ECFFF", "Glow color as a hex value, for example #9ECFFF, #FF8A2A, or #FFFFFF.");
		if (enableGunnrRune.Value)
		{
			PrefabManager.OnVanillaPrefabsAvailable += AddGunnrRuneItem;
		}
		harmony.PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Gunnr Companion loaded.");
	}

	private void OnDestroy()
	{
		PrefabManager.OnVanillaPrefabsAvailable -= AddGunnrRuneItem;
		harmony.UnpatchSelf();
		if ((Object)(object)instance == (Object)(object)this)
		{
			instance = null;
		}
	}

	private void Update()
	{
		if (!enableGunnr.Value)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return;
		}
		EnsureGunnrRuneInInventory(localPlayer);
		if (Time.time < nextCompanionCheckTime)
		{
			return;
		}
		nextCompanionCheckTime = Time.time + 2f;
		List<Character> list = FindAllGunnrs();
		Character primaryGunnr = GetPrimaryGunnr(localPlayer, list);
		if (removeDuplicates.Value && list.Count > 1)
		{
			RemoveExtraGunnrs(list, primaryGunnr);
		}
		if (summonGunnrNow.Value)
		{
			summonGunnrNow.Value = false;
			if (gunnrStored.Value)
			{
				ReleaseStoredGunnr(localPlayer);
				return;
			}
			if ((Object)(object)primaryGunnr == (Object)null)
			{
				SpawnGunnr(localPlayer);
				return;
			}
			SummonExistingGunnr(localPlayer, primaryGunnr);
		}
		if (spawnGunnrNow.Value)
		{
			spawnGunnrNow.Value = false;
			if (gunnrStored.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Spawn Gunnr Now was pressed, but " + companionName.Value + " is currently stored inside Gunnr's Rune. Use the rune or Summon Gunnr Now to release her."));
				return;
			}
			if (!CanSpawnOrTeleportNow(localPlayer))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Spawn Gunnr Now was pressed, but " + companionName.Value + " was not spawned because water safety is active."));
				return;
			}
			if ((Object)(object)primaryGunnr == (Object)null)
			{
				SpawnGunnr(localPlayer);
				return;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Spawn Gunnr Now was pressed, but " + companionName.Value + " already exists. No duplicate was spawned."));
		}
		if (gunnrStored.Value)
		{
			if ((Object)(object)primaryGunnr != (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)(companionName.Value + " is marked as stored, but a live Gunnr was found. Removing the live copy to prevent duplicates."));
				RemoveGunnr(primaryGunnr);
			}
		}
		else if ((Object)(object)primaryGunnr == (Object)null)
		{
			if (!manualSpawnOnly.Value && spawnIfMissing.Value && CanSpawnOrTeleportNow(localPlayer))
			{
				SpawnGunnr(localPlayer);
			}
		}
		else
		{
			ConfigureGunnr(((Component)primaryGunnr).gameObject, primaryGunnr);
			if (CanSpawnOrTeleportNow(localPlayer))
			{
				MaintainGunnr(localPlayer, primaryGunnr);
			}
		}
	}

	private void AddGunnrRuneItem()
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Expected O, but got Unknown
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Expected O, but got Unknown
		try
		{
			ItemConfig val = new ItemConfig();
			val.Name = "Gunnr's Rune";
			val.Description = "Bound by oath, carried by blood. Use to call Gunnr to your side or send her beyond sight.";
			val.Amount = 1;
			val.CraftingStation = CraftingStations.Workbench;
			val.AddRequirement("TrophyWolf", 1, 0);
			val.AddRequirement("BoneFragments", 5, 0);
			val.AddRequirement("Resin", 5, 0);
			CustomItem val2 = new CustomItem("GunnrsRune", "Wishbone", val);
			ItemManager.Instance.AddItem(val2);
			ConfigureRunePrefab(val2.ItemPrefab);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Gunnr's Rune item registered.");
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to register Gunnr's Rune: " + ex.Message));
		}
		finally
		{
			PrefabManager.OnVanillaPrefabsAvailable -= AddGunnrRuneItem;
		}
	}

	private static void ConfigureRunePrefab(GameObject runePrefab)
	{
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)runePrefab == (Object)null))
		{
			ItemDrop component = runePrefab.GetComponent<ItemDrop>();
			if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null)
			{
				SharedData shared = component.m_itemData.m_shared;
				shared.m_name = "Gunnr's Rune";
				shared.m_description = "Bound by oath, carried by blood. Use to call Gunnr to your side or send her beyond sight.";
				shared.m_itemType = (ItemType)2;
				shared.m_maxStackSize = 1;
				shared.m_useDurability = false;
				shared.m_destroyBroken = false;
				TrySetObjectMember(shared, "m_consumeStatusEffect", null);
				TrySetObjectMember(shared, "m_equipStatusEffect", null);
			}
		}
	}

	private void EnsureGunnrRuneInInventory(Player player)
	{
		if ((Object)(object)player == (Object)null || !enableGunnrRune.Value || !giveRuneIfMissing.Value)
		{
			return;
		}
		Inventory inventory = ((Humanoid)player).GetInventory();
		if (inventory != null && !InventoryHasGunnrRune(inventory))
		{
			GameObject gunnrRunePrefab = GetGunnrRunePrefab();
			if (!((Object)(object)gunnrRunePrefab == (Object)null) && TryAddItemToInventory(inventory, gunnrRunePrefab))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Added Gunnr's Rune to the player inventory.");
			}
		}
	}

	private static bool TryAddItemToInventory(Inventory inventory, GameObject itemPrefab)
	{
		if (inventory == null || (Object)(object)itemPrefab == (Object)null)
		{
			return false;
		}
		MethodInfo[] methods = typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		MethodInfo[] array = methods;
		foreach (MethodInfo methodInfo in array)
		{
			if (methodInfo == null || methodInfo.Name != "AddItem")
			{
				continue;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			if (parameters.Length != 2 || !(parameters[0].ParameterType == typeof(GameObject)) || !(parameters[1].ParameterType == typeof(int)))
			{
				continue;
			}
			try
			{
				object obj = methodInfo.Invoke(inventory, new object[2] { itemPrefab, 1 });
				if (obj is bool result)
				{
					return result;
				}
				return obj != null;
			}
			catch
			{
				return false;
			}
		}
		return false;
	}

	private static GameObject GetGunnrRunePrefab()
	{
		if ((Object)(object)ObjectDB.instance != (Object)null)
		{
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("GunnrsRune");
			if ((Object)(object)itemPrefab != (Object)null)
			{
				return itemPrefab;
			}
		}
		if ((Object)(object)ZNetScene.instance != (Object)null)
		{
			GameObject prefab = ZNetScene.instance.GetPrefab("GunnrsRune");
			if ((Object)(object)prefab != (Object)null)
			{
				return prefab;
			}
		}
		return null;
	}

	private static bool InventoryHasGunnrRune(Inventory inventory)
	{
		if (inventory == null)
		{
			return false;
		}
		foreach (ItemData allItem in inventory.GetAllItems())
		{
			if (IsGunnrRuneItem(allItem))
			{
				return true;
			}
		}
		return false;
	}

	private void HandleGunnrRuneUse(Player player)
	{
		if (!((Object)(object)player == (Object)null) && enableGunnrRune.Value && !(Time.time < nextRuneUseTime))
		{
			nextRuneUseTime = Time.time + Mathf.Max(0f, runeUseCooldown.Value);
			List<Character> list = FindAllGunnrs();
			Character primaryGunnr = GetPrimaryGunnr(player, list);
			if (removeDuplicates.Value && list.Count > 1)
			{
				RemoveExtraGunnrs(list, primaryGunnr);
			}
			if (gunnrStored.Value)
			{
				ReleaseStoredGunnr(player);
			}
			else if ((Object)(object)primaryGunnr != (Object)null)
			{
				StoreGunnrInRune(primaryGunnr);
			}
			else
			{
				ReleaseStoredGunnr(player);
			}
		}
	}

	private void StoreGunnrInRune(Character gunnr)
	{
		if (!((Object)(object)gunnr == (Object)null))
		{
			storedGunnrHealth.Value = GetCharacterHealth(gunnr);
			gunnrStored.Value = true;
			RemoveGunnr(gunnr);
			((BaseUnityPlugin)this).Logger.LogInfo((object)(companionName.Value + " was bound into Gunnr's Rune."));
			MessageLocalPlayer(companionName.Value + " returns to the rune.");
		}
	}

	private void ReleaseStoredGunnr(Player player)
	{
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		if (!CanSpawnOrTeleportNow(player))
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Gunnr's Rune was used, but " + companionName.Value + " cannot be released here because water safety is active."));
			MessageLocalPlayer(companionName.Value + " cannot answer here.");
			return;
		}
		Character val = SpawnGunnr(player);
		if ((Object)(object)val == (Object)null)
		{
			MessageLocalPlayer(companionName.Value + " cannot answer here.");
			return;
		}
		if (storedGunnrHealth.Value > 0f)
		{
			TrySetCharacterHealth(val, storedGunnrHealth.Value);
		}
		gunnrStored.Value = false;
		storedGunnrHealth.Value = 0f;
		((BaseUnityPlugin)this).Logger.LogInfo((object)(companionName.Value + " was released from Gunnr's Rune."));
		MessageLocalPlayer(companionName.Value + " answers the rune.");
	}

	private static bool IsGunnrRuneItem(ItemData item)
	{
		if (item == null)
		{
			return false;
		}
		if ((Object)(object)item.m_dropPrefab != (Object)null)
		{
			string name = ((Object)item.m_dropPrefab).name;
			if (!string.IsNullOrEmpty(name) && name.StartsWith("GunnrsRune", StringComparison.Ordinal))
			{
				return true;
			}
		}
		if (item.m_shared != null)
		{
			return item.m_shared.m_name == "Gunnr's Rune" || item.m_shared.m_name == "$item_gunnrsrune";
		}
		return false;
	}

	private static float GetCharacterHealth(Character character)
	{
		if ((Object)(object)character == (Object)null)
		{
			return 0f;
		}
		object raw = InvokeInstanceMethodWithResult(character, "GetHealth");
		if (TryConvertToFloat(raw, out var value))
		{
			return value;
		}
		return 0f;
	}

	private static void TrySetCharacterHealth(Character character, float health)
	{
		if (!((Object)(object)character == (Object)null) && !(health <= 0f))
		{
			InvokeInstanceMethod(character, "SetHealth", health);
		}
	}

	private static void MessageLocalPlayer(string message)
	{
		if (!((Object)(object)Player.m_localPlayer == (Object)null) && !string.IsNullOrEmpty(message))
		{
			InvokeInstanceMethod(Player.m_localPlayer, "Message", (object)(MessageType)2, message, 0, null);
		}
	}

	private bool CanSpawnOrTeleportNow(Player player)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null)
		{
			return false;
		}
		if (avoidWaterSpawn.Value && IsPositionOverWater(((Component)player).transform.position))
		{
			return false;
		}
		return true;
	}

	private Character SpawnGunnr(Player player)
	{
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return null;
		}
		GameObject prefab = ZNetScene.instance.GetPrefab("Wolf");
		if ((Object)(object)prefab == (Object)null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find Wolf prefab. Gunnr was not spawned.");
			return null;
		}
		if (!TryGetCompanionPositionNearPlayer(player, out var position))
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Could not find a safe non-water spawn point for " + companionName.Value + ". Spawn skipped."));
			return null;
		}
		Quaternion val = Quaternion.LookRotation(((Component)player).transform.forward);
		GameObject val2 = Object.Instantiate<GameObject>(prefab, position, val);
		((Object)val2).name = "Gunnr_Companion";
		Character component = val2.GetComponent<Character>();
		if ((Object)(object)component == (Object)null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Spawned Wolf prefab had no Character component.");
			return null;
		}
		MarkAsGunnr(component);
		ConfigureGunnr(val2, component);
		gunnrStored.Value = false;
		((BaseUnityPlugin)this).Logger.LogInfo((object)$"Spawned {companionName.Value} as a level {companionLevel.Value} wolf.");
		return component;
	}

	private void MaintainGunnr(Player player, Character gunnr)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: 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)
		if ((Object)(object)player == (Object)null || (Object)(object)gunnr == (Object)null)
		{
			return;
		}
		ConfigureGunnr(((Component)gunnr).gameObject, gunnr);
		if (teleportDistance.Value <= 0f)
		{
			return;
		}
		float num = Vector3.Distance(((Component)player).transform.position, ((Component)gunnr).transform.position);
		if (num > teleportDistance.Value)
		{
			if (!TryGetCompanionPositionNearPlayer(player, out var position))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Skipped moving " + companionName.Value + "; no safe non-water position was found."));
				return;
			}
			((Component)gunnr).transform.position = position;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Moved " + companionName.Value + " back near the player."));
		}
	}

	private void SummonExistingGunnr(Player player, Character gunnr)
	{
		//IL_008e: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)player == (Object)null) && !((Object)(object)gunnr == (Object)null))
		{
			if (!CanSpawnOrTeleportNow(player))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Summon Gunnr Now was pressed, but " + companionName.Value + " was not moved because water safety is active."));
				return;
			}
			if (!TryGetCompanionPositionNearPlayer(player, out var position))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Summon Gunnr Now was pressed, but no safe land position was found.");
				return;
			}
			ConfigureGunnr(((Component)gunnr).gameObject, gunnr);
			((Component)gunnr).transform.position = position;
			((Component)gunnr).transform.rotation = Quaternion.LookRotation(((Component)player).transform.forward);
			StopGunnrMovement(gunnr);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Summoned existing " + companionName.Value + " to the player without spawning a duplicate."));
		}
	}

	private static void StopGunnrMovement(Character gunnr)
	{
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)gunnr == (Object)null)
		{
			return;
		}
		InvokeInstanceMethod(gunnr, "StopMoving");
		InvokeInstanceMethod(gunnr, "Stop");
		Component[] componentsInChildren = ((Component)gunnr).GetComponentsInChildren<Component>(true);
		Component[] array = componentsInChildren;
		foreach (Component val in array)
		{
			if (!((Object)(object)val == (Object)null) && !(((object)val).GetType().Name != "Rigidbody"))
			{
				TrySetVector3Member(val, "velocity", Vector3.zero);
				TrySetVector3Member(val, "angularVelocity", Vector3.zero);
			}
		}
	}

	private static bool TrySetVector3Member(object target, string memberName, Vector3 value)
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		if (target == null || string.IsNullOrEmpty(memberName))
		{
			return false;
		}
		Type type = target.GetType();
		FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (field != null && field.FieldType == typeof(Vector3))
		{
			field.SetValue(target, value);
			return true;
		}
		PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (property != null && property.PropertyType == typeof(Vector3) && property.CanWrite)
		{
			property.SetValue(target, value, null);
			return true;
		}
		return false;
	}

	private bool TryGetCompanionPositionNearPlayer(Player player, out Vector3 position)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: 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)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_00a7: 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_00b2: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: 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_0128: 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_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0170: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0187: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0233: Unknown result type (might be due to invalid IL or missing references)
		//IL_0207: Unknown result type (might be due to invalid IL or missing references)
		//IL_0248: Unknown result type (might be due to invalid IL or missing references)
		//IL_024a: Unknown result type (might be due to invalid IL or missing references)
		//IL_021b: Unknown result type (might be due to invalid IL or missing references)
		//IL_021d: Unknown result type (might be due to invalid IL or missing references)
		position = Vector3.zero;
		if ((Object)(object)player == (Object)null)
		{
			return false;
		}
		float num = Mathf.Max(1f, spawnDistance.Value);
		Vector3 position2 = ((Component)player).transform.position;
		Vector3 right = ((Component)player).transform.right;
		Vector3 forward = ((Component)player).transform.forward;
		Vector3[] obj = new Vector3[12]
		{
			right * num + forward * 1.25f,
			-right * num + forward * 1.25f,
			right * num - forward * 1.25f,
			-right * num - forward * 1.25f,
			forward * num,
			-forward * num,
			right * num,
			-right * num,
			default(Vector3),
			default(Vector3),
			default(Vector3),
			default(Vector3)
		};
		Vector3 val = right + forward;
		obj[8] = ((Vector3)(ref val)).normalized * num;
		val = -right + forward;
		obj[9] = ((Vector3)(ref val)).normalized * num;
		val = right - forward;
		obj[10] = ((Vector3)(ref val)).normalized * num;
		val = -right - forward;
		obj[11] = ((Vector3)(ref val)).normalized * num;
		Vector3[] array = (Vector3[])(object)obj;
		Vector3[] array2 = array;
		foreach (Vector3 val2 in array2)
		{
			Vector3 val3 = position2 + val2;
			if (avoidWaterSpawn.Value && IsPositionOverWater(val3))
			{
				continue;
			}
			if (TrySnapToGround(val3, out var groundedPosition))
			{
				if (!avoidWaterSpawn.Value || !IsPositionOverWater(groundedPosition))
				{
					position = groundedPosition;
					return true;
				}
			}
			else if (!avoidWaterSpawn.Value || !IsPositionOverWater(val3))
			{
				position = val3;
				return true;
			}
		}
		return false;
	}

	private static bool TrySnapToGround(Vector3 position, out Vector3 groundedPosition)
	{
		//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_0008: 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_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		groundedPosition = position;
		if (TryGetGroundHeight(position, out var groundHeight))
		{
			groundedPosition = new Vector3(position.x, groundHeight + 0.15f, position.z);
			return true;
		}
		return false;
	}

	private static bool IsPlayerSafelyOnLand(Player player)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null)
		{
			return false;
		}
		if (IsPositionOverWater(((Component)player).transform.position))
		{
			return false;
		}
		if (TryGetGroundHeight(((Component)player).transform.position, out var groundHeight))
		{
			return ((Component)player).transform.position.y >= groundHeight - 1f && ((Component)player).transform.position.y <= groundHeight + 5f;
		}
		return true;
	}

	private static bool IsPositionOverWater(Vector3 position)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		if (!TryGetWaterLevel(out var waterLevel))
		{
			return false;
		}
		if (TryGetGroundHeight(position, out var groundHeight))
		{
			return groundHeight + waterHeightBuffer.Value < waterLevel;
		}
		return position.y < waterLevel + waterHeightBuffer.Value;
	}

	private static bool TryGetGroundHeight(Vector3 position, out float groundHeight)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		groundHeight = position.y;
		object obj = ZoneSystem.instance;
		if (obj == null)
		{
			return false;
		}
		MethodInfo[] methods = obj.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		MethodInfo[] array = methods;
		foreach (MethodInfo methodInfo in array)
		{
			if (methodInfo == null || methodInfo.Name != "GetGroundHeight")
			{
				continue;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			try
			{
				if (parameters.Length == 1 && parameters[0].ParameterType == typeof(Vector3))
				{
					object raw = methodInfo.Invoke(obj, new object[1] { position });
					if (TryConvertToFloat(raw, out groundHeight))
					{
						return true;
					}
				}
				if (parameters.Length == 2 && parameters[0].ParameterType == typeof(Vector3) && parameters[1].ParameterType.IsByRef)
				{
					object[] array2 = new object[2] { position, 0f };
					if ((!(methodInfo.Invoke(obj, array2) is bool flag) || flag) && TryConvertToFloat(array2[1], out groundHeight))
					{
						return true;
					}
				}
			}
			catch
			{
			}
		}
		return false;
	}

	private static bool TryGetWaterLevel(out float waterLevel)
	{
		waterLevel = 30f;
		object obj = ZoneSystem.instance;
		if (obj == null)
		{
			return false;
		}
		string[] array = new string[3] { "m_waterLevel", "WaterLevel", "waterLevel" };
		string[] array2 = array;
		foreach (string memberName in array2)
		{
			if (TryGetFloatMember(obj, memberName, out waterLevel))
			{
				return true;
			}
		}
		waterLevel = 30f;
		return true;
	}

	private static bool TryGetFloatMember(object target, string memberName, out float value)
	{
		value = 0f;
		if (target == null || string.IsNullOrEmpty(memberName))
		{
			return false;
		}
		Type type = target.GetType();
		FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (field != null && TryConvertToFloat(field.GetValue(target), out value))
		{
			return true;
		}
		PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (property != null && TryConvertToFloat(property.GetValue(target, null), out value))
		{
			return true;
		}
		return false;
	}

	private static bool TryConvertToFloat(object raw, out float value)
	{
		value = 0f;
		if (raw is float num)
		{
			value = num;
			return true;
		}
		if (raw is double num2)
		{
			value = (float)num2;
			return true;
		}
		if (raw is int num3)
		{
			value = num3;
			return true;
		}
		return false;
	}

	private static List<Character> FindAllGunnrs()
	{
		List<Character> list = new List<Character>();
		Character[] array = Object.FindObjectsByType<Character>((FindObjectsSortMode)0);
		Character[] array2 = array;
		foreach (Character val in array2)
		{
			if (IsGunnr(val))
			{
				list.Add(val);
			}
		}
		return list;
	}

	private static Character GetPrimaryGunnr(Player player, List<Character> gunnrs)
	{
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		if (gunnrs == null || gunnrs.Count == 0)
		{
			return null;
		}
		if ((Object)(object)player == (Object)null)
		{
			return gunnrs[0];
		}
		Character val = null;
		float num = float.MaxValue;
		foreach (Character gunnr in gunnrs)
		{
			if (!((Object)(object)gunnr == (Object)null))
			{
				float num2 = Vector3.Distance(((Component)player).transform.position, ((Component)gunnr).transform.position);
				if (num2 < num)
				{
					num = num2;
					val = gunnr;
				}
			}
		}
		return ((Object)(object)val != (Object)null) ? val : gunnrs[0];
	}

	private void RemoveExtraGunnrs(List<Character> gunnrs, Character keepGunnr)
	{
		if (gunnrs == null || gunnrs.Count <= 1)
		{
			return;
		}
		foreach (Character gunnr in gunnrs)
		{
			if (!((Object)(object)gunnr == (Object)null) && !((Object)(object)gunnr == (Object)(object)keepGunnr))
			{
				RemoveGunnr(gunnr);
			}
		}
	}

	private void RemoveGunnr(Character gunnr)
	{
		if (!((Object)(object)gunnr == (Object)null))
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Removing duplicate Gunnr: " + ((Object)((Component)gunnr).gameObject).name));
			ZNetView component = ((Component)gunnr).GetComponent<ZNetView>();
			if (!((Object)(object)component != (Object)null) || !InvokeInstanceMethod(component, "Destroy"))
			{
				Object.Destroy((Object)(object)((Component)gunnr).gameObject);
			}
		}
	}

	public static bool IsGunnr(Character character)
	{
		if ((Object)(object)character == (Object)null)
		{
			return false;
		}
		ZNetView component = ((Component)character).GetComponent<ZNetView>();
		if ((Object)(object)component == (Object)null || component.GetZDO() == null)
		{
			return false;
		}
		return component.GetZDO().GetBool("Ulf_Gunnr_Companion", false);
	}

	private static void MarkAsGunnr(Character character)
	{
		if (!((Object)(object)character == (Object)null))
		{
			ZNetView component = ((Component)character).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.GetZDO() != null)
			{
				component.GetZDO().Set("Ulf_Gunnr_Companion", true);
				component.GetZDO().Set("Ulf_Gunnr_Name", companionName.Value);
			}
		}
	}

	private static void ConfigureGunnr(GameObject wolfObject, Character wolfCharacter)
	{
		if ((Object)(object)wolfObject == (Object)null || (Object)(object)wolfCharacter == (Object)null)
		{
			return;
		}
		MarkAsGunnr(wolfCharacter);
		InvokeInstanceMethod(wolfCharacter, "SetLevel", companionLevel.Value);
		ApplyAppearance(wolfObject);
		Component[] componentsInChildren = wolfObject.GetComponentsInChildren<Component>(true);
		Component[] array = componentsInChildren;
		foreach (Component val in array)
		{
			if (!((Object)(object)val == (Object)null))
			{
				string name = ((object)val).GetType().Name;
				if (name == "Tameable")
				{
					InvokeInstanceMethod(val, "Tame");
					InvokeInstanceMethod(val, "SetTamed", true);
					InvokeInstanceMethod(val, "SetName", companionName.Value);
				}
				if (name == "MonsterAI" || name == "BaseAI")
				{
					InvokeInstanceMethod(val, "SetTamed", true);
				}
			}
		}
		SetLikelyTameNameZdoValues(wolfCharacter, companionName.Value);
		ApplyAuras(wolfObject);
	}

	private static void ApplyAppearance(GameObject wolfObject)
	{
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: 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_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: 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)
		if (!((Object)(object)wolfObject == (Object)null) && wolfPupMode != null && wolfPupScale != null && adultWolfScale != null)
		{
			float num = (wolfPupMode.Value ? wolfPupScale.Value : adultWolfScale.Value);
			num = Mathf.Clamp(num, 0.25f, 2f);
			Vector3 val = Vector3.one * num;
			Vector3 val2 = wolfObject.transform.localScale - val;
			if (((Vector3)(ref val2)).sqrMagnitude > 0.0001f)
			{
				wolfObject.transform.localScale = val;
			}
		}
	}

	private static void ApplyAuras(GameObject wolfObject)
	{
		if (!((Object)(object)wolfObject == (Object)null))
		{
			if (enableAuras == null || !enableAuras.Value)
			{
				RemoveAuraChild(wolfObject, "Ulf_Gunnr_FireAura");
				RemoveAuraChild(wolfObject, "Ulf_Gunnr_FrostAura");
				RemoveAuraChild(wolfObject, "Ulf_Gunnr_SpiritAura");
				RemoveAuraChild(wolfObject, "Ulf_Gunnr_GlowAura");
			}
			else
			{
				SyncPrefabAura(wolfObject, "Ulf_Gunnr_FireAura", fireAura.Value, fireAuraPrefab.Value, FireAuraCandidates);
				SyncPrefabAura(wolfObject, "Ulf_Gunnr_FrostAura", frostAura.Value, frostAuraPrefab.Value, FrostAuraCandidates);
				SyncPrefabAura(wolfObject, "Ulf_Gunnr_SpiritAura", spiritAura.Value, spiritAuraPrefab.Value, SpiritAuraCandidates);
				SyncGlowAura(wolfObject);
			}
		}
	}

	private static void SyncPrefabAura(GameObject wolfObject, string childName, bool enabled, string configuredPrefabName, string[] autoCandidates)
	{
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: 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)
		Transform val = FindDirectChild(wolfObject.transform, childName);
		if (!enabled)
		{
			if ((Object)(object)val != (Object)null)
			{
				SafeDestroy(((Component)val).gameObject);
			}
			return;
		}
		if ((Object)(object)val != (Object)null)
		{
			val.localPosition = new Vector3(0f, auraYOffset.Value, 0f);
			val.localRotation = Quaternion.identity;
			val.localScale = Vector3.one * auraScale.Value;
			return;
		}
		GameObject val2 = ResolveAuraPrefab(configuredPrefabName, autoCandidates);
		if ((Object)(object)val2 == (Object)null)
		{
			ManualLogSource obj = log;
			if (obj != null)
			{
				obj.LogWarning((object)("Could not find aura prefab for " + childName + ". Set its prefab config to a valid Valheim VFX prefab name."));
			}
			return;
		}
		GameObject val3 = Object.Instantiate<GameObject>(val2, wolfObject.transform);
		((Object)val3).name = childName;
		val3.transform.localPosition = new Vector3(0f, auraYOffset.Value, 0f);
		val3.transform.localRotation = Quaternion.identity;
		val3.transform.localScale = Vector3.one * auraScale.Value;
		ManualLogSource obj2 = log;
		if (obj2 != null)
		{
			obj2.LogInfo((object)("Attached aura " + childName + " using prefab " + ((Object)val2).name + "."));
		}
	}

	private static GameObject ResolveAuraPrefab(string configuredPrefabName, string[] autoCandidates)
	{
		if ((Object)(object)ZNetScene.instance == (Object)null)
		{
			return null;
		}
		if (!string.IsNullOrWhiteSpace(configuredPrefabName) && !configuredPrefabName.Equals("Auto", StringComparison.OrdinalIgnoreCase))
		{
			GameObject prefab = ZNetScene.instance.GetPrefab(configuredPrefabName.Trim());
			if ((Object)(object)prefab != (Object)null)
			{
				return prefab;
			}
		}
		foreach (string text in autoCandidates)
		{
			GameObject prefab2 = ZNetScene.instance.GetPrefab(text);
			if ((Object)(object)prefab2 != (Object)null)
			{
				return prefab2;
			}
		}
		return null;
	}

	private static void SyncGlowAura(GameObject wolfObject)
	{
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Expected O, but got Unknown
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		Transform val = FindDirectChild(wolfObject.transform, "Ulf_Gunnr_GlowAura");
		if (!glowAura.Value)
		{
			if ((Object)(object)val != (Object)null)
			{
				SafeDestroy(((Component)val).gameObject);
			}
			return;
		}
		GameObject val2;
		if ((Object)(object)val == (Object)null)
		{
			val2 = new GameObject("Ulf_Gunnr_GlowAura");
			val2.transform.SetParent(wolfObject.transform, false);
		}
		else
		{
			val2 = ((Component)val).gameObject;
		}
		val2.transform.localPosition = new Vector3(0f, auraYOffset.Value + 0.4f, 0f);
		val2.transform.localRotation = Quaternion.identity;
		val2.transform.localScale = Vector3.one;
		Light val3 = val2.GetComponent<Light>();
		if ((Object)(object)val3 == (Object)null)
		{
			val3 = val2.AddComponent<Light>();
		}
		val3.type = (LightType)2;
		val3.range = glowRange.Value;
		val3.intensity = glowIntensity.Value;
		val3.color = ParseHexColor(glowColorHex.Value, new Color(0.62f, 0.81f, 1f, 1f));
		val3.shadows = (LightShadows)0;
	}

	private static Color ParseHexColor(string hex, Color fallback)
	{
		//IL_000c: 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_0050: 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_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrWhiteSpace(hex))
		{
			return fallback;
		}
		string text = hex.Trim();
		if (!text.StartsWith("#"))
		{
			text = "#" + text;
		}
		Color result = default(Color);
		if (ColorUtility.TryParseHtmlString(text, ref result))
		{
			return result;
		}
		return fallback;
	}

	private static Transform FindDirectChild(Transform parent, string childName)
	{
		if ((Object)(object)parent == (Object)null || string.IsNullOrEmpty(childName))
		{
			return null;
		}
		for (int i = 0; i < parent.childCount; i++)
		{
			Transform child = parent.GetChild(i);
			if ((Object)(object)child != (Object)null && ((Object)child).name == childName)
			{
				return child;
			}
		}
		return null;
	}

	private static void RemoveAuraChild(GameObject wolfObject, string childName)
	{
		if (!((Object)(object)wolfObject == (Object)null))
		{
			Transform val = FindDirectChild(wolfObject.transform, childName);
			if ((Object)(object)val != (Object)null)
			{
				SafeDestroy(((Component)val).gameObject);
			}
		}
	}

	private static void SafeDestroy(GameObject gameObject)
	{
		if (!((Object)(object)gameObject == (Object)null))
		{
			Object.Destroy((Object)(object)gameObject);
		}
	}

	private static void SetLikelyTameNameZdoValues(Character wolfCharacter, string name)
	{
		if (!((Object)(object)wolfCharacter == (Object)null) && !string.IsNullOrEmpty(name))
		{
			ZNetView component = ((Component)wolfCharacter).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.GetZDO() != null)
			{
				component.GetZDO().Set("TamedName", name);
				component.GetZDO().Set("tamedName", name);
				component.GetZDO().Set("name", name);
				component.GetZDO().Set("Ulf_Gunnr_Name", name);
			}
		}
	}

	private static bool TrySetObjectMember(object target, string memberName, object value)
	{
		if (target == null || string.IsNullOrEmpty(memberName))
		{
			return false;
		}
		Type type = target.GetType();
		FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (field != null)
		{
			field.SetValue(target, value);
			return true;
		}
		PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (property != null && property.CanWrite)
		{
			property.SetValue(target, value, null);
			return true;
		}
		return false;
	}

	private static object InvokeInstanceMethodWithResult(object target, string methodName, params object[] args)
	{
		if (target == null || string.IsNullOrEmpty(methodName))
		{
			return null;
		}
		Type type = target.GetType();
		MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		MethodInfo[] array = methods;
		foreach (MethodInfo methodInfo in array)
		{
			if (methodInfo == null || methodInfo.Name != methodName)
			{
				continue;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			if (parameters.Length == args.Length)
			{
				try
				{
					return methodInfo.Invoke(target, args);
				}
				catch
				{
					return null;
				}
			}
		}
		return null;
	}

	private static bool InvokeInstanceMethod(object target, string methodName, params object[] args)
	{
		if (target == null || string.IsNullOrEmpty(methodName))
		{
			return false;
		}
		Type type = target.GetType();
		MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		MethodInfo[] array = methods;
		foreach (MethodInfo methodInfo in array)
		{
			if (methodInfo == null || methodInfo.Name != methodName)
			{
				continue;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			if (parameters.Length == args.Length)
			{
				try
				{
					methodInfo.Invoke(target, args);
					return true;
				}
				catch
				{
					return false;
				}
			}
		}
		return false;
	}
}