Decompiled source of The Lyngvi Wardens v1.0.0

TheLyngviWardens.dll

Decompiled 11 hours 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 System.Security.Permissions;
using BepInEx;
using HarmonyLib;
using Jotunn;
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("Soulbound Companion")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Soulbound Companion")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
namespace SoulboundCompanion;

internal enum WardenRole
{
	Tank,
	Berserker,
	Archer
}
[BepInPlugin("com.forlore.soulboundcompanion", "The Lyngvi Wardens", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
internal class SoulboundCompanionPlugin : BaseUnityPlugin
{
	private sealed class WardenState
	{
		public int Slot;

		public WardenRole Role;

		public GameObject Object;

		public MonsterAI AI;

		public bool Spawned;

		public float SpawnDelay;

		public bool RespawnPending;

		public float RespawnTimer;

		public long RespawnOwnerId;

		public bool ReportedPersistentSearch;

		public float MovementCheckTimer;

		public float StuckTime;

		public Vector3 LastPosition;

		public float PatrolTimer;

		public Vector3 PatrolAnchor;

		public bool HasPatrolPoint;

		public bool WasInCombat;

		public bool SoftLeashIssued;

		public WardenState(int slot, WardenRole role)
		{
			Slot = slot;
			Role = role;
		}
	}

	private sealed class ProgressionPower
	{
		public int Stage;

		public int Level;

		public float ElementalDamage;

		public string Name;

		public ProgressionPower(int stage, int level, float elementalDamage, string name)
		{
			Stage = stage;
			Level = level;
			ElementalDamage = elementalDamage;
			Name = name;
		}
	}

	[HarmonyPatch(typeof(Character), "OnDeath")]
	private static class WardenDeathPatch
	{
		private static void Prefix(Character __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			LyngviWardenMarker component = ((Component)__instance).GetComponent<LyngviWardenMarker>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			ZNetView component2 = ((Component)__instance).GetComponent<ZNetView>();
			if ((Object)(object)component2 == (Object)null || !component2.IsValid())
			{
				return;
			}
			ZDO zDO = component2.GetZDO();
			if (zDO == null)
			{
				return;
			}
			long num = zDO.GetLong("SoulboundCompanion_OwnerId", 0L);
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && num == localPlayer.GetPlayerID())
			{
				int slot = zDO.GetInt("SoulboundCompanion_Slot", component.Slot);
				zDO.Set("SoulboundCompanion_IsSoulbound", false);
				if ((Object)(object)Instance != (Object)null)
				{
					Instance.ScheduleRespawn(slot, num);
				}
			}
		}
	}

	[HarmonyPatch(typeof(Character), "Damage", new Type[] { typeof(HitData) })]
	private static class WardenElementalDamagePatch
	{
		private static void Prefix(HitData hit)
		{
			if (hit == null || !hit.HaveAttacker())
			{
				return;
			}
			Character attacker = hit.GetAttacker();
			if ((Object)(object)attacker == (Object)null)
			{
				return;
			}
			LyngviWardenMarker component = ((Component)attacker).GetComponent<LyngviWardenMarker>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			float elementalDamage = component.ElementalDamage;
			if (!(elementalDamage <= 0f))
			{
				switch (component.Slot)
				{
				case 0:
					hit.m_damage.m_poison += elementalDamage;
					break;
				case 1:
					hit.m_damage.m_fire += elementalDamage;
					break;
				case 2:
					hit.m_damage.m_frost += elementalDamage;
					break;
				}
			}
		}
	}

	[HarmonyPatch(typeof(MonsterAI), "Awake")]
	private static class MonsterAIAwakePatch
	{
		private static void Postfix(MonsterAI __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null || !zDO.GetBool("SoulboundCompanion_IsSoulbound", false))
			{
				return;
			}
			int num = zDO.GetInt("SoulboundCompanion_Slot", -1);
			if (num >= 0 && num <= 2)
			{
				WardenRole role = (WardenRole)zDO.GetInt("SoulboundCompanion_Role", num);
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && zDO.GetLong("SoulboundCompanion_OwnerId", 0L) == localPlayer.GetPlayerID())
				{
					EnsureWardenState(((Component)__instance).gameObject, num, role, localPlayer);
				}
				else
				{
					EnsureWardenState(((Component)__instance).gameObject, num, role, null);
				}
			}
		}
	}

	public const string PluginGUID = "com.forlore.soulboundcompanion";

	public const string PluginName = "The Lyngvi Wardens";

	public const string PluginVersion = "1.0.0";

	public const string BodvarName = "Bodvar";

	public const string StarkadName = "Starkad";

	public const string OrvarName = "Orvar";

	private const float BodvarBaseHealth = 70f;

	private const float StarkadBaseHealth = 45f;

	private const float OrvarBaseHealth = 40f;

	public const float RespawnDelaySeconds = 20f;

	public const float RegenerationPercentPerSecond = 0.02f;

	public const float RegenerationTickInterval = 1f;

	private const float ProgressionCheckInterval = 2f;

	private float _progressionCheckTimer;

	private int _activeProgressionStage = -1;

	private const float MeleePatrolMinDistance = 8f;

	private const float MeleePatrolMaxDistance = 14f;

	private const float ArcherPatrolMinDistance = 11f;

	private const float ArcherPatrolMaxDistance = 17f;

	private const float InteriorMeleePatrolMinDistance = 3f;

	private const float InteriorMeleePatrolMaxDistance = 5f;

	private const float InteriorArcherPatrolMinDistance = 4f;

	private const float InteriorArcherPatrolMaxDistance = 6f;

	private const float PatrolTimeMin = 8f;

	private const float PatrolTimeMax = 14f;

	private const float PatrolRecenterDistance = 12f;

	private const float SoftLeashDistance = 30f;

	private const float SoftLeashReleaseDistance = 24f;

	private const float HardTeleportDistance = 70f;

	private const float StuckCheckDistance = 32f;

	private const float MovementCheckInterval = 1f;

	private const float MinimumMovement = 0.35f;

	private const float StuckTimeBeforeTeleport = 8f;

	private const float InteriorCheckInterval = 0.25f;

	private const float InteriorTransferDelay = 0.6f;

	private const float PostInteriorTransferGrace = 2f;

	private float _interiorCheckTimer;

	private bool _hasInteriorState;

	private bool _playerWasInInterior;

	private bool _interiorTransferPending;

	private bool _pendingInteriorState;

	private float _interiorTransferTimer;

	private float _postInteriorTransferGraceTimer;

	private const string BodvarPrefabName = "LyngviWarden_Bodvar";

	private const string StarkadPrefabName = "LyngviWarden_Starkad";

	private const string OrvarPrefabName = "LyngviWarden_Orvar";

	private const string LegacyWarriorPrefabName = "SoulboundCompanion_Warrior";

	private const string LegacyArcherPrefabName = "SoulboundCompanion_Archer";

	private const string AncientLegacyPrefabName = "Skeleton_NoArcher";

	private bool _legacyMigrationDone;

	private bool _prefabsReady;

	internal const string ZdoSoulbound = "SoulboundCompanion_IsSoulbound";

	internal const string ZdoOwnerId = "SoulboundCompanion_OwnerId";

	internal const string ZdoOwnerName = "SoulboundCompanion_OwnerName";

	internal const string ZdoSlot = "SoulboundCompanion_Slot";

	internal const string ZdoRole = "SoulboundCompanion_Role";

	internal const string ZdoScaledLevel = "SoulboundCompanion_ScaledLevel";

	internal const string ZdoElementalDamage = "SoulboundCompanion_ElementalDamage";

	internal static SoulboundCompanionPlugin Instance;

	private Harmony _harmony;

	private Player _currentPlayer;

	private WardenState[] _wardens;

	private void Awake()
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Expected O, but got Unknown
		Instance = this;
		_wardens = new WardenState[3]
		{
			new WardenState(0, WardenRole.Tank),
			new WardenState(1, WardenRole.Berserker),
			new WardenState(2, WardenRole.Archer)
		};
		_harmony = new Harmony("com.forlore.soulboundcompanion");
		_harmony.PatchAll();
		PrefabManager.OnVanillaPrefabsAvailable += CreateWardenPrefabs;
		Logger.LogInfo((object)"The Lyngvi Wardens have awakened!");
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		PrefabManager.OnVanillaPrefabsAvailable -= CreateWardenPrefabs;
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	private void CreateWardenPrefabs()
	{
		CreateBodvarPrefab();
		CreateStarkadPrefab();
		CreateOrvarPrefab();
		_prefabsReady = (Object)(object)PrefabManager.Instance.GetPrefab("LyngviWarden_Bodvar") != (Object)null && (Object)(object)PrefabManager.Instance.GetPrefab("LyngviWarden_Starkad") != (Object)null && (Object)(object)PrefabManager.Instance.GetPrefab("LyngviWarden_Orvar") != (Object)null;
		if (_prefabsReady)
		{
			Logger.LogInfo((object)"Bodvar, Starkad and Orvar prefabs are ready.");
			PrefabManager.OnVanillaPrefabsAvailable -= CreateWardenPrefabs;
		}
	}

	private void CreateBodvarPrefab()
	{
		GameObject prefab = PrefabManager.Instance.GetPrefab("LyngviWarden_Bodvar");
		if ((Object)(object)prefab != (Object)null)
		{
			return;
		}
		prefab = PrefabManager.Instance.CreateClonedPrefab("LyngviWarden_Bodvar", "Skeleton_Poison");
		if ((Object)(object)prefab == (Object)null)
		{
			Logger.LogError((object)"Failed to create Bodvar from Skeleton_Poison.");
			return;
		}
		Humanoid component = prefab.GetComponent<Humanoid>();
		Character component2 = prefab.GetComponent<Character>();
		if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
		{
			Logger.LogError((object)"Bodvar prefab is missing Humanoid/Character.");
			return;
		}
		component.m_defaultItems = (GameObject[])(object)new GameObject[0];
		component.m_randomWeapon = ResolvePrefabs("skeleton_mace");
		component.m_randomArmor = (GameObject[])(object)new GameObject[0];
		component.m_randomShield = ResolvePrefabs("ShieldBronzeBuckler");
		component2.m_name = "Bodvar";
		component2.m_health = 70f;
		component2.m_boss = false;
		DisableDrops(prefab);
		PrefabManager.Instance.AddPrefab(prefab);
		Logger.LogInfo((object)"Created Bodvar: Rancid Remains body, mace + bronze buckler.");
	}

	private void CreateStarkadPrefab()
	{
		GameObject prefab = PrefabManager.Instance.GetPrefab("LyngviWarden_Starkad");
		if ((Object)(object)prefab != (Object)null)
		{
			return;
		}
		prefab = PrefabManager.Instance.CreateClonedPrefab("LyngviWarden_Starkad", "Skeleton_Hildir");
		if ((Object)(object)prefab == (Object)null)
		{
			Logger.LogError((object)"Failed to create Starkad from Skeleton_Hildir.");
			return;
		}
		Humanoid component = prefab.GetComponent<Humanoid>();
		Character component2 = prefab.GetComponent<Character>();
		if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
		{
			Logger.LogError((object)"Starkad prefab is missing Humanoid/Character.");
			return;
		}
		component.m_defaultItems = (GameObject[])(object)new GameObject[0];
		component.m_randomWeapon = ResolvePrefabs("skeleton_sword_hildir");
		component.m_randomArmor = (GameObject[])(object)new GameObject[0];
		component.m_randomShield = (GameObject[])(object)new GameObject[0];
		component2.m_name = "Starkad";
		component2.m_health = 45f;
		component2.m_boss = false;
		DisableDrops(prefab);
		PrefabManager.Instance.AddPrefab(prefab);
		Logger.LogInfo((object)"Created Starkad: burning Brenna body, fixed flaming sword, no fire nova.");
	}

	private void CreateOrvarPrefab()
	{
		GameObject prefab = PrefabManager.Instance.GetPrefab("LyngviWarden_Orvar");
		if ((Object)(object)prefab != (Object)null)
		{
			return;
		}
		prefab = PrefabManager.Instance.CreateClonedPrefab("LyngviWarden_Orvar", "Skeleton");
		if ((Object)(object)prefab == (Object)null)
		{
			Logger.LogError((object)"Failed to create Orvar from Skeleton.");
			return;
		}
		Humanoid component = prefab.GetComponent<Humanoid>();
		Character component2 = prefab.GetComponent<Character>();
		if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
		{
			Logger.LogError((object)"Orvar prefab is missing Humanoid/Character.");
			return;
		}
		component.m_defaultItems = (GameObject[])(object)new GameObject[0];
		component.m_randomWeapon = ResolvePrefabs("skeleton_bow");
		component.m_randomArmor = (GameObject[])(object)new GameObject[0];
		component.m_randomShield = (GameObject[])(object)new GameObject[0];
		component2.m_name = "Orvar";
		component2.m_health = 40f;
		component2.m_boss = false;
		DisableDrops(prefab);
		PrefabManager.Instance.AddPrefab(prefab);
		Logger.LogInfo((object)"Created Orvar: fixed bow loadout.");
	}

	private void DisableDrops(GameObject prefab)
	{
		CharacterDrop component = prefab.GetComponent<CharacterDrop>();
		if ((Object)(object)component != (Object)null)
		{
			component.m_dropsEnabled = false;
		}
	}

	private GameObject[] ResolvePrefabs(params string[] prefabNames)
	{
		List<GameObject> list = new List<GameObject>();
		foreach (string text in prefabNames)
		{
			GameObject prefab = PrefabManager.Instance.GetPrefab(text);
			if ((Object)(object)prefab != (Object)null)
			{
				list.Add(prefab);
			}
			else
			{
				Logger.LogWarning((object)("Could not resolve prefab '" + text + "'."));
			}
		}
		return list.ToArray();
	}

	private void Update()
	{
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			ResetRuntimeStateForNoPlayer();
		}
		else
		{
			if (!_prefabsReady)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)_currentPlayer != (Object)(object)localPlayer)
			{
				InitializeForPlayer(localPlayer);
			}
			if (_legacyMigrationDone || TryMigrateLegacyEntourage(localPlayer))
			{
				UpdateProgressionScaling(localPlayer, forceImmediate: false);
				UpdateInteriorTracking(localPlayer);
				UpdatePendingInteriorTransfer(localPlayer);
				WardenState[] wardens = _wardens;
				foreach (WardenState state in wardens)
				{
					UpdateWardenSlot(state, localPlayer);
				}
			}
		}
	}

	private void ResetRuntimeStateForNoPlayer()
	{
		_currentPlayer = null;
		_legacyMigrationDone = false;
		_activeProgressionStage = -1;
		_progressionCheckTimer = 0f;
		_hasInteriorState = false;
		_interiorTransferPending = false;
		_interiorTransferTimer = 0f;
		_postInteriorTransferGraceTimer = 0f;
		if (_wardens != null)
		{
			WardenState[] wardens = _wardens;
			foreach (WardenState wardenState in wardens)
			{
				ClearLoadedInstance(wardenState);
				wardenState.ReportedPersistentSearch = false;
			}
		}
	}

	private void InitializeForPlayer(Player player)
	{
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		_currentPlayer = player;
		_legacyMigrationDone = false;
		long playerID = player.GetPlayerID();
		WardenState[] wardens = _wardens;
		foreach (WardenState wardenState in wardens)
		{
			ClearLoadedInstance(wardenState);
			wardenState.SpawnDelay = 3f;
			wardenState.StuckTime = 0f;
			wardenState.ReportedPersistentSearch = false;
			wardenState.PatrolTimer = 0f;
			wardenState.HasPatrolPoint = false;
			wardenState.WasInCombat = false;
			wardenState.SoftLeashIssued = false;
			if (wardenState.RespawnPending && wardenState.RespawnOwnerId != playerID)
			{
				wardenState.RespawnPending = false;
				wardenState.RespawnTimer = 0f;
				wardenState.RespawnOwnerId = 0L;
			}
		}
		_activeProgressionStage = -1;
		_progressionCheckTimer = 0f;
		UpdateProgressionScaling(player, forceImmediate: true);
		bool flag = Character.InInterior(((Component)player).transform.position);
		_hasInteriorState = true;
		_playerWasInInterior = flag;
		_pendingInteriorState = flag;
		_interiorTransferPending = false;
		_interiorTransferTimer = 0f;
		_interiorCheckTimer = 0f;
		_postInteriorTransferGraceTimer = 0f;
		Logger.LogInfo((object)$"Player detected. Interior: {flag}. Preparing the Lyngvi Wardens...");
	}

	private bool TryMigrateLegacyEntourage(Player player)
	{
		if ((Object)(object)player == (Object)null || ZDOMan.instance == null)
		{
			return false;
		}
		long playerID = player.GetPlayerID();
		int num = 0;
		num += RemoveLegacyPrefab("SoulboundCompanion_Warrior", playerID);
		num += RemoveLegacyPrefab("SoulboundCompanion_Archer", playerID);
		num += RemoveLegacyPrefab("Skeleton_NoArcher", playerID);
		_legacyMigrationDone = true;
		if (num > 0)
		{
			Logger.LogInfo((object)$"Migrated from the old Soulbound Companion system. Removed {num} legacy companion(s).");
		}
		return true;
	}

	private int RemoveLegacyPrefab(string prefabName, long playerId)
	{
		//IL_0104: 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)
		int num = 0;
		List<ZDO> allZDOsWithPrefab = GetAllZDOsWithPrefab(prefabName);
		foreach (ZDO item in allZDOsWithPrefab)
		{
			if (item == null || !item.IsValid() || !item.GetBool("SoulboundCompanion_IsSoulbound", false) || item.GetLong("SoulboundCompanion_OwnerId", 0L) != playerId)
			{
				continue;
			}
			item.Set("SoulboundCompanion_IsSoulbound", false);
			try
			{
				GameObject val = null;
				if ((Object)(object)ZNetScene.instance != (Object)null)
				{
					val = ZNetScene.instance.FindInstance(item.m_uid);
				}
				if ((Object)(object)val != (Object)null && (Object)(object)ZNetScene.instance != (Object)null)
				{
					ZNetScene.instance.Destroy(val);
				}
				else
				{
					item.SetOwner(ZDOMan.GetSessionID());
					ZDOMan.instance.DestroyZDO(item);
				}
				num++;
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)$"Could not completely destroy legacy ZDO {item.m_uid}: {ex.Message}");
			}
		}
		return num;
	}

	private void UpdateProgressionScaling(Player player, bool forceImmediate)
	{
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		if (!forceImmediate)
		{
			_progressionCheckTimer -= Time.deltaTime;
			if (_progressionCheckTimer > 0f)
			{
				return;
			}
		}
		_progressionCheckTimer = 2f;
		ProgressionPower progressionPower = GetProgressionPower(player);
		if (forceImmediate || progressionPower.Stage != _activeProgressionStage)
		{
			_activeProgressionStage = progressionPower.Stage;
			Logger.LogInfo((object)$"Lyngvi Warden progression: {progressionPower.Name} | Level {progressionPower.Level} | Elemental bonus +{progressionPower.ElementalDamage:0}");
			ApplyProgressionScalingToLoadedWardens();
		}
	}

	private static ProgressionPower GetProgressionPower(Player player)
	{
		if ((Object)(object)player == (Object)null)
		{
			return GetProgressionPowerForStage(0);
		}
		List<string> uniqueKeys = player.GetUniqueKeys();
		HashSet<string> hashSet = ((uniqueKeys != null) ? new HashSet<string>(uniqueKeys) : new HashSet<string>());
		if (hashSet.Contains("defeated_fader"))
		{
			return GetProgressionPowerForStage(7);
		}
		if (hashSet.Contains("defeated_queen"))
		{
			return GetProgressionPowerForStage(6);
		}
		if (hashSet.Contains("defeated_goblinking"))
		{
			return GetProgressionPowerForStage(5);
		}
		if (hashSet.Contains("defeated_dragon"))
		{
			return GetProgressionPowerForStage(4);
		}
		if (hashSet.Contains("defeated_bonemass"))
		{
			return GetProgressionPowerForStage(3);
		}
		if (hashSet.Contains("defeated_gdking"))
		{
			return GetProgressionPowerForStage(2);
		}
		if (hashSet.Contains("defeated_eikthyr"))
		{
			return GetProgressionPowerForStage(1);
		}
		return GetProgressionPowerForStage(0);
	}

	private static ProgressionPower GetProgressionPowerForStage(int stage)
	{
		return stage switch
		{
			0 => new ProgressionPower(0, 2, 10f, "Meadows / Unproven"), 
			1 => new ProgressionPower(1, 5, 15f, "Black Forest / Eikthyr defeated"), 
			2 => new ProgressionPower(2, 7, 20f, "Swamp / Elder defeated"), 
			3 => new ProgressionPower(3, 9, 25f, "Mountain / Bonemass defeated"), 
			4 => new ProgressionPower(4, 11, 30f, "Plains / Moder defeated"), 
			5 => new ProgressionPower(5, 13, 35f, "Mistlands / Yagluth defeated"), 
			6 => new ProgressionPower(6, 15, 40f, "Ashlands / Queen defeated"), 
			_ => new ProgressionPower(7, 17, 45f, "Deep North / Fader defeated"), 
		};
	}

	private ProgressionPower GetActiveProgressionPower()
	{
		if (_activeProgressionStage < 0)
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				return GetProgressionPower(Player.m_localPlayer);
			}
			return GetProgressionPowerForStage(0);
		}
		return GetProgressionPowerForStage(_activeProgressionStage);
	}

	private void ApplyProgressionScalingToLoadedWardens()
	{
		if (_wardens == null)
		{
			return;
		}
		WardenState[] wardens = _wardens;
		foreach (WardenState wardenState in wardens)
		{
			if (wardenState.Spawned && !((Object)(object)wardenState.Object == (Object)null))
			{
				ApplyProgressionPower(wardenState.Object, preserveHealthPercentage: true);
			}
		}
	}

	private void ApplyProgressionPower(GameObject warden, bool preserveHealthPercentage)
	{
		if ((Object)(object)warden == (Object)null)
		{
			return;
		}
		Character component = warden.GetComponent<Character>();
		if ((Object)(object)component == (Object)null || component.IsDead())
		{
			return;
		}
		ZNetView component2 = warden.GetComponent<ZNetView>();
		if ((Object)(object)component2 != (Object)null && component2.IsValid() && !component2.IsOwner())
		{
			LyngviWardenMarker component3 = warden.GetComponent<LyngviWardenMarker>();
			ZDO zDO = component2.GetZDO();
			if ((Object)(object)component3 != (Object)null && zDO != null)
			{
				component3.SetElementalDamage(zDO.GetFloat("SoulboundCompanion_ElementalDamage", 10f));
			}
			return;
		}
		ProgressionPower activeProgressionPower = GetActiveProgressionPower();
		float num = 1f;
		float maxHealth = component.GetMaxHealth();
		float health = component.GetHealth();
		if (preserveHealthPercentage && maxHealth > 0f && health > 0f)
		{
			num = Mathf.Clamp01(health / maxHealth);
		}
		component.SetLevel(activeProgressionPower.Level);
		float maxHealth2 = component.GetMaxHealth();
		if (preserveHealthPercentage)
		{
			component.SetHealth(Mathf.Max(1f, maxHealth2 * num));
		}
		else
		{
			component.SetHealth(maxHealth2);
		}
		LyngviWardenMarker component4 = warden.GetComponent<LyngviWardenMarker>();
		if ((Object)(object)component4 != (Object)null)
		{
			component4.SetElementalDamage(activeProgressionPower.ElementalDamage);
		}
		if ((Object)(object)component2 != (Object)null && component2.IsValid())
		{
			ZDO zDO2 = component2.GetZDO();
			if (zDO2 != null)
			{
				zDO2.Set("SoulboundCompanion_ScaledLevel", activeProgressionPower.Level);
				zDO2.Set("SoulboundCompanion_ElementalDamage", activeProgressionPower.ElementalDamage);
			}
		}
	}

	private void UpdateInteriorTracking(Player player)
	{
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		_interiorCheckTimer -= Time.deltaTime;
		if (!(_interiorCheckTimer > 0f))
		{
			_interiorCheckTimer = 0.25f;
			bool flag = Character.InInterior(((Component)player).transform.position);
			if (!_hasInteriorState)
			{
				_hasInteriorState = true;
				_playerWasInInterior = flag;
			}
			else if (flag != _playerWasInInterior)
			{
				_playerWasInInterior = flag;
				_pendingInteriorState = flag;
				_interiorTransferPending = true;
				_interiorTransferTimer = 0.6f;
				Logger.LogInfo((object)(flag ? "Player entered an interior. Lyngvi Wardens transfer scheduled." : "Player left an interior. Lyngvi Wardens transfer scheduled."));
			}
			else if (!_interiorTransferPending)
			{
				EnsureLoadedWardensMatchInterior(player, flag);
			}
		}
	}

	private void UpdatePendingInteriorTransfer(Player player)
	{
		if (_postInteriorTransferGraceTimer > 0f)
		{
			_postInteriorTransferGraceTimer -= Time.deltaTime;
		}
		if (_interiorTransferPending)
		{
			_interiorTransferTimer -= Time.deltaTime;
			if (!(_interiorTransferTimer > 0f))
			{
				_interiorTransferPending = false;
				_interiorTransferTimer = 0f;
				TransferEntireEntourageAcrossInteriorBoundary(player, _pendingInteriorState);
				_postInteriorTransferGraceTimer = 2f;
			}
		}
	}

	private void EnsureLoadedWardensMatchInterior(Player player, bool playerInside)
	{
		//IL_0040: 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_0080: 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)
		WardenState[] wardens = _wardens;
		foreach (WardenState wardenState in wardens)
		{
			if (!wardenState.RespawnPending && !((Object)(object)wardenState.Object == (Object)null))
			{
				bool flag = Character.InInterior(wardenState.Object.transform.position);
				if (flag != playerInside)
				{
					Logger.LogInfo((object)(GetDisplayName(wardenState.Slot) + " is on the wrong side of an interior boundary. Correcting..."));
					Vector3 returnPosition = GetReturnPosition(player, wardenState.Slot, playerInside);
					TransferLoadedWarden(wardenState, player, returnPosition);
				}
			}
		}
	}

	private void TransferEntireEntourageAcrossInteriorBoundary(Player player, bool targetInside)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: 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_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		long playerID = player.GetPlayerID();
		int num = 0;
		int num2 = 0;
		WardenState[] wardens = _wardens;
		foreach (WardenState wardenState in wardens)
		{
			if (wardenState.RespawnPending)
			{
				continue;
			}
			Vector3 returnPosition = GetReturnPosition(player, wardenState.Slot, targetInside);
			if ((Object)(object)wardenState.Object != (Object)null)
			{
				TransferLoadedWarden(wardenState, player, returnPosition);
				num++;
				continue;
			}
			ZDO val = FindSavedWardenZdo(wardenState, playerID, ((Component)player).transform.position);
			if (val == null)
			{
				wardenState.SpawnDelay = 0.5f;
				continue;
			}
			StampWardenIdentity(val, wardenState, player);
			GameObject val2 = null;
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				val2 = ZNetScene.instance.FindInstance(val.m_uid);
			}
			if ((Object)(object)val2 != (Object)null)
			{
				AdoptWarden(wardenState, val2, player);
				TransferLoadedWarden(wardenState, player, returnPosition);
				num++;
				continue;
			}
			val.SetPosition(returnPosition);
			val.Set(ZDOVars.s_follow, string.Empty);
			ZDOMan instance = ZDOMan.instance;
			if (instance != null)
			{
				instance.ForceSendZDO(val.m_uid);
			}
			wardenState.SpawnDelay = 0.5f;
			wardenState.ReportedPersistentSearch = false;
			num2++;
		}
		Logger.LogInfo((object)$"Lyngvi Wardens interior transfer completed. Loaded moved: {num}, saved/unloaded moved: {num2}.");
	}

	private void TransferLoadedWarden(WardenState state, Player player, Vector3 targetPosition)
	{
		//IL_002e: 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_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: 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)
		if ((Object)(object)state.Object == (Object)null || (Object)(object)player == (Object)null)
		{
			return;
		}
		state.Object.transform.position = targetPosition;
		state.Object.transform.rotation = ((Component)player).transform.rotation;
		ZNetView component = state.Object.GetComponent<ZNetView>();
		if ((Object)(object)component != (Object)null && component.IsValid())
		{
			ZDO zDO = component.GetZDO();
			if (zDO != null)
			{
				StampWardenIdentity(zDO, state, player);
				zDO.SetPosition(targetPosition);
				zDO.Set(ZDOVars.s_follow, string.Empty);
				ZDOMan instance = ZDOMan.instance;
				if (instance != null)
				{
					instance.ForceSendZDO(zDO.m_uid);
				}
			}
		}
		if ((Object)(object)state.AI == (Object)null)
		{
			state.AI = state.Object.GetComponent<MonsterAI>();
		}
		if ((Object)(object)state.AI != (Object)null)
		{
			((Behaviour)state.AI).enabled = true;
		}
		state.LastPosition = targetPosition;
		state.StuckTime = 0f;
		state.MovementCheckTimer = 1f;
		state.SoftLeashIssued = false;
		AssignNewPatrolPoint(state, player, immediate: true);
	}

	private bool IsWardenInCombat(WardenState state)
	{
		if (state == null || (Object)(object)state.AI == (Object)null)
		{
			return false;
		}
		BaseAI aI = (BaseAI)(object)state.AI;
		return aI.IsAlerted() || aI.HaveTarget();
	}

	private void AssignNewPatrolPoint(WardenState state, Player player, bool immediate)
	{
		//IL_003c: 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_0054: 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_00cc: 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)
		if (state == null || (Object)(object)state.Object == (Object)null || (Object)(object)state.AI == (Object)null || (Object)(object)player == (Object)null)
		{
			return;
		}
		bool insideInterior = Character.InInterior(((Component)player).transform.position);
		Vector3 patrolPosition = GetPatrolPosition(player, state.Slot, insideInterior);
		state.AI.SetFollowTarget((GameObject)null);
		BaseAI aI = (BaseAI)(object)state.AI;
		aI.ResetPatrolPoint();
		aI.SetPatrolPoint(patrolPosition);
		ZNetView component = state.Object.GetComponent<ZNetView>();
		if ((Object)(object)component != (Object)null && component.IsValid())
		{
			ZDO zDO = component.GetZDO();
			if (zDO != null)
			{
				zDO.Set(ZDOVars.s_follow, string.Empty);
			}
		}
		state.PatrolAnchor = ((Component)player).transform.position;
		state.HasPatrolPoint = true;
		state.PatrolTimer = (immediate ? Random.Range(4f, 7f) : Random.Range(8f, 14f));
	}

	private void UpdatePeacefulPatrol(WardenState state, Player player, float distanceFromPlayer)
	{
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: 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_01c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		if (state == null || (Object)(object)state.Object == (Object)null || (Object)(object)state.AI == (Object)null || (Object)(object)player == (Object)null)
		{
			return;
		}
		if (IsWardenInCombat(state))
		{
			state.WasInCombat = true;
			state.StuckTime = 0f;
			return;
		}
		if (state.WasInCombat)
		{
			state.WasInCombat = false;
			state.PatrolTimer = 0f;
			state.SoftLeashIssued = false;
		}
		if (distanceFromPlayer <= 24f)
		{
			state.SoftLeashIssued = false;
		}
		if (distanceFromPlayer > 30f && !state.SoftLeashIssued)
		{
			state.SoftLeashIssued = true;
			AssignNewPatrolPoint(state, player, immediate: true);
			return;
		}
		state.MovementCheckTimer -= Time.deltaTime;
		if (state.MovementCheckTimer <= 0f)
		{
			state.MovementCheckTimer = 1f;
			float num = Vector3.Distance(state.Object.transform.position, state.LastPosition);
			if (distanceFromPlayer >= 32f && num < 0.35f)
			{
				state.StuckTime += 1f;
			}
			else
			{
				state.StuckTime = 0f;
			}
			state.LastPosition = state.Object.transform.position;
			if (state.StuckTime >= 8f)
			{
				CatchUpToPlayer(state, player, "stuck far from player");
				return;
			}
		}
		state.PatrolTimer -= Time.deltaTime;
		bool flag = !state.HasPatrolPoint || Vector3.Distance(((Component)player).transform.position, state.PatrolAnchor) >= 12f;
		if (state.PatrolTimer <= 0f || flag)
		{
			AssignNewPatrolPoint(state, player, immediate: false);
		}
	}

	private static Vector3 GetPatrolPosition(Player player, int slot, bool insideInterior)
	{
		//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_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_00c1: 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_00e5: 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_011c: 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)
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: 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)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: 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_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0164: 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_0193: 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_018f: Unknown result type (might be due to invalid IL or missing references)
		float num;
		float num2;
		if (insideInterior)
		{
			if (slot == 2)
			{
				num = 4f;
				num2 = 6f;
			}
			else
			{
				num = 3f;
				num2 = 5f;
			}
		}
		else if (slot == 2)
		{
			num = 11f;
			num2 = 17f;
		}
		else
		{
			num = 8f;
			num2 = 14f;
		}
		float num3 = Random.Range(num, num2);
		float num4 = slot switch
		{
			0 => -90f, 
			1 => 90f, 
			_ => 180f, 
		} + Random.Range(-65f, 65f);
		Vector3 val = Quaternion.AngleAxis(num4, Vector3.up) * ((Component)player).transform.forward;
		val.y = 0f;
		((Vector3)(ref val)).Normalize();
		Vector3 val2 = ((Component)player).transform.position + val * num3;
		if (insideInterior)
		{
			val2.y = ((Component)player).transform.position.y + 0.1f;
			return val2;
		}
		Vector3 val3 = val2 + Vector3.up * 5f;
		RaycastHit val4 = default(RaycastHit);
		if (Physics.Raycast(val3, Vector3.down, ref val4, 15f))
		{
			val2 = ((RaycastHit)(ref val4)).point + Vector3.up * 0.2f;
		}
		else
		{
			val2.y = ((Component)player).transform.position.y + 0.5f;
		}
		return val2;
	}

	private void UpdateWardenSlot(WardenState state, Player player)
	{
		if (_interiorTransferPending)
		{
			return;
		}
		if (state.RespawnPending)
		{
			state.RespawnTimer -= Time.deltaTime;
			if (!(state.RespawnTimer > 0f))
			{
				state.RespawnPending = false;
				state.RespawnTimer = 0f;
				state.RespawnOwnerId = 0L;
				Logger.LogInfo((object)(GetDisplayName(state.Slot) + "'s soul has reformed."));
				SpawnNewWarden(state, player);
			}
		}
		else if (state.Spawned && (Object)(object)state.Object == (Object)null)
		{
			ClearLoadedInstance(state);
			state.SpawnDelay = 0.5f;
		}
		else if (state.Spawned)
		{
			UpdateWardenMovement(state, player);
		}
		else
		{
			state.SpawnDelay -= Time.deltaTime;
			if (!(state.SpawnDelay > 0f) && !TryReconnectExistingWarden(state, player))
			{
				SpawnNewWarden(state, player);
			}
		}
	}

	internal void ScheduleRespawn(int slot, long ownerId)
	{
		WardenState state = GetState(slot);
		if (state != null && !state.RespawnPending)
		{
			state.RespawnPending = true;
			state.RespawnTimer = 20f;
			state.RespawnOwnerId = ownerId;
			ClearLoadedInstance(state);
			Logger.LogInfo((object)$"{GetDisplayName(slot)} was destroyed. Soul reforming in {20f} seconds.");
		}
	}

	private WardenState GetState(int slot)
	{
		if (_wardens == null)
		{
			return null;
		}
		WardenState[] wardens = _wardens;
		foreach (WardenState wardenState in wardens)
		{
			if (wardenState.Slot == slot)
			{
				return wardenState;
			}
		}
		return null;
	}

	private static string GetPrefabNameForSlot(int slot)
	{
		return slot switch
		{
			0 => "LyngviWarden_Bodvar", 
			1 => "LyngviWarden_Starkad", 
			2 => "LyngviWarden_Orvar", 
			_ => "LyngviWarden_Bodvar", 
		};
	}

	private List<ZDO> GetCandidateZdosForState(WardenState state)
	{
		return GetAllZDOsWithPrefab(GetPrefabNameForSlot(state.Slot));
	}

	private ZDO FindSavedWardenZdo(WardenState state, long playerId, Vector3 referencePosition)
	{
		//IL_003c: 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)
		List<ZDO> candidateZdosForState = GetCandidateZdosForState(state);
		ZDO result = null;
		float num = float.MaxValue;
		foreach (ZDO item in candidateZdosForState)
		{
			if (DoesZdoBelongToSlot(item, state, playerId))
			{
				float num2 = Vector3.Distance(item.GetPosition(), referencePosition);
				if (num2 < num)
				{
					num = num2;
					result = item;
				}
			}
		}
		return result;
	}

	private bool TryReconnectExistingWarden(WardenState state, Player player)
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0246: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02be: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0273: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0296: Unknown result type (might be due to invalid IL or missing references)
		//IL_029b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null)
		{
			state.SpawnDelay = 1f;
			return true;
		}
		long playerID = player.GetPlayerID();
		bool flag = Character.InInterior(((Component)player).transform.position);
		LyngviWardenMarker[] array = Object.FindObjectsByType<LyngviWardenMarker>((FindObjectsSortMode)0);
		LyngviWardenMarker[] array2 = array;
		foreach (LyngviWardenMarker lyngviWardenMarker in array2)
		{
			if ((Object)(object)lyngviWardenMarker == (Object)null)
			{
				continue;
			}
			ZNetView component = ((Component)lyngviWardenMarker).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				continue;
			}
			ZDO zDO = component.GetZDO();
			if (!DoesZdoBelongToSlot(zDO, state, playerID))
			{
				continue;
			}
			Character component2 = ((Component)lyngviWardenMarker).GetComponent<Character>();
			if (!((Object)(object)component2 != (Object)null) || !component2.IsDead())
			{
				StampWardenIdentity(zDO, state, player);
				AdoptWarden(state, ((Component)lyngviWardenMarker).gameObject, player);
				bool flag2 = Character.InInterior(((Component)lyngviWardenMarker).transform.position);
				if (flag2 != flag)
				{
					Vector3 returnPosition = GetReturnPosition(player, state.Slot, flag);
					TransferLoadedWarden(state, player, returnPosition);
				}
				return true;
			}
		}
		List<ZDO> candidateZdosForState = GetCandidateZdosForState(state);
		ZDO val = null;
		int num = 0;
		float num2 = float.MaxValue;
		foreach (ZDO item in candidateZdosForState)
		{
			if (DoesZdoBelongToSlot(item, state, playerID))
			{
				num++;
				float num3 = Vector3.Distance(item.GetPosition(), ((Component)player).transform.position);
				if (num3 < num2)
				{
					num2 = num3;
					val = item;
				}
			}
		}
		if (num > 1)
		{
			Logger.LogWarning((object)$"Found {num} saved copies of {GetDisplayName(state.Slot)}. Using the closest.");
		}
		if (val == null)
		{
			return false;
		}
		StampWardenIdentity(val, state, player);
		GameObject val2 = ZNetScene.instance.FindInstance(val.m_uid);
		if ((Object)(object)val2 != (Object)null)
		{
			AdoptWarden(state, val2, player);
			bool flag3 = Character.InInterior(val2.transform.position);
			if (flag3 != flag)
			{
				Vector3 returnPosition2 = GetReturnPosition(player, state.Slot, flag);
				TransferLoadedWarden(state, player, returnPosition2);
			}
			return true;
		}
		Vector3 returnPosition3 = GetReturnPosition(player, state.Slot, flag);
		val.SetPosition(returnPosition3);
		val.Set(ZDOVars.s_follow, string.Empty);
		ZDOMan.instance.ForceSendZDO(val.m_uid);
		if (!state.ReportedPersistentSearch)
		{
			Logger.LogInfo((object)("Saved " + GetDisplayName(state.Slot) + " found. Calling them back..."));
			state.ReportedPersistentSearch = true;
		}
		state.SpawnDelay = 0.5f;
		return true;
	}

	private static bool DoesZdoBelongToSlot(ZDO zdo, WardenState state, long playerId)
	{
		if (zdo == null || !zdo.IsValid())
		{
			return false;
		}
		if (!zdo.GetBool("SoulboundCompanion_IsSoulbound", false))
		{
			return false;
		}
		if (zdo.GetLong("SoulboundCompanion_OwnerId", 0L) != playerId)
		{
			return false;
		}
		return zdo.GetInt("SoulboundCompanion_Slot", -1) == state.Slot;
	}

	private static void StampWardenIdentity(ZDO zdo, WardenState state, Player player)
	{
		if (zdo != null)
		{
			zdo.Set("SoulboundCompanion_IsSoulbound", true);
			zdo.Set("SoulboundCompanion_Slot", state.Slot);
			zdo.Set("SoulboundCompanion_Role", (int)state.Role);
			if ((Object)(object)player != (Object)null)
			{
				zdo.Set("SoulboundCompanion_OwnerId", player.GetPlayerID());
				zdo.Set("SoulboundCompanion_OwnerName", player.GetPlayerName());
			}
			zdo.Set(ZDOVars.s_follow, string.Empty);
			zdo.Set(ZDOVars.s_tamed, true);
			zdo.Persistent = true;
		}
	}

	private static List<ZDO> GetAllZDOsWithPrefab(string prefabName)
	{
		List<ZDO> list = new List<ZDO>();
		if (ZDOMan.instance == null)
		{
			return list;
		}
		List<ZDO> list2 = new List<ZDO>();
		int num = 0;
		bool flag = false;
		while (!flag)
		{
			list2.Clear();
			flag = ZDOMan.instance.GetAllZDOsWithPrefabIterative(prefabName, list2, ref num);
			foreach (ZDO item in list2)
			{
				if (item != null)
				{
					list.Add(item);
				}
			}
		}
		return list;
	}

	private void SpawnNewWarden(WardenState state, Player player)
	{
		//IL_008b: 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_00a3: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		string prefabNameForSlot = GetPrefabNameForSlot(state.Slot);
		GameObject prefab = PrefabManager.Instance.GetPrefab(prefabNameForSlot);
		if ((Object)(object)prefab == (Object)null)
		{
			Logger.LogError((object)("Could not find prefab '" + prefabNameForSlot + "' for " + GetDisplayName(state.Slot) + "."));
			state.SpawnDelay = 5f;
			return;
		}
		bool insideInterior = Character.InInterior(((Component)player).transform.position);
		Vector3 returnPosition = GetReturnPosition(player, state.Slot, insideInterior);
		GameObject val = Object.Instantiate<GameObject>(prefab, returnPosition, ((Component)player).transform.rotation);
		if ((Object)(object)val == (Object)null)
		{
			state.SpawnDelay = 5f;
			return;
		}
		ZNetView component = val.GetComponent<ZNetView>();
		if ((Object)(object)component == (Object)null || !component.IsValid())
		{
			Object.Destroy((Object)(object)val);
			state.SpawnDelay = 5f;
			return;
		}
		ZDO zDO = component.GetZDO();
		if (zDO == null)
		{
			Object.Destroy((Object)(object)val);
			state.SpawnDelay = 5f;
			return;
		}
		component.m_persistent = true;
		StampWardenIdentity(zDO, state, player);
		EnsureWardenState(val, state.Slot, state.Role, player);
		AdoptWarden(state, val, player);
		ProgressionPower activeProgressionPower = GetActiveProgressionPower();
		Logger.LogInfo((object)$"{GetDisplayName(state.Slot)} manifested at {activeProgressionPower.Name}: level {activeProgressionPower.Level}, elemental bonus +{activeProgressionPower.ElementalDamage:0}.");
	}

	private static void EnsureWardenState(GameObject warden, int slot, WardenRole role, Player player)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)warden == (Object)null)
		{
			return;
		}
		Character component = warden.GetComponent<Character>();
		MonsterAI component2 = warden.GetComponent<MonsterAI>();
		if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
		{
			return;
		}
		component.m_name = GetDisplayName(slot);
		component.m_faction = (Faction)0;
		component.m_tamed = true;
		component.SetTamed(true);
		component.m_boss = false;
		LyngviWardenMarker lyngviWardenMarker = warden.GetComponent<LyngviWardenMarker>();
		if ((Object)(object)lyngviWardenMarker == (Object)null)
		{
			lyngviWardenMarker = warden.AddComponent<LyngviWardenMarker>();
		}
		lyngviWardenMarker.Configure(slot, role);
		CharacterDrop component3 = warden.GetComponent<CharacterDrop>();
		if ((Object)(object)component3 != (Object)null)
		{
			component3.m_dropsEnabled = false;
		}
		ZNetView component4 = warden.GetComponent<ZNetView>();
		if ((Object)(object)component4 != (Object)null && component4.IsValid())
		{
			ZDO zDO = component4.GetZDO();
			if (zDO != null)
			{
				component4.m_persistent = true;
				zDO.Persistent = true;
				zDO.Set("SoulboundCompanion_IsSoulbound", true);
				zDO.Set("SoulboundCompanion_Slot", slot);
				zDO.Set("SoulboundCompanion_Role", (int)role);
				zDO.Set(ZDOVars.s_tamed, true);
				zDO.Set(ZDOVars.s_follow, string.Empty);
				if ((Object)(object)player != (Object)null)
				{
					zDO.Set("SoulboundCompanion_OwnerId", player.GetPlayerID());
					zDO.Set("SoulboundCompanion_OwnerName", player.GetPlayerName());
				}
				lyngviWardenMarker.SetElementalDamage(zDO.GetFloat("SoulboundCompanion_ElementalDamage", 10f));
			}
		}
		if ((Object)(object)Instance != (Object)null)
		{
			Instance.ApplyProgressionPower(warden, preserveHealthPercentage: true);
		}
		((Behaviour)component2).enabled = true;
		component2.SetFollowTarget((GameObject)null);
	}

	private void AdoptWarden(WardenState state, GameObject warden, Player player)
	{
		//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)
		state.Object = warden;
		state.AI = warden.GetComponent<MonsterAI>();
		if ((Object)(object)state.AI == (Object)null)
		{
			state.Spawned = false;
			return;
		}
		EnsureWardenState(warden, state.Slot, state.Role, player);
		state.LastPosition = warden.transform.position;
		state.MovementCheckTimer = 1f;
		state.StuckTime = 0f;
		state.Spawned = true;
		state.ReportedPersistentSearch = false;
		state.WasInCombat = false;
		state.SoftLeashIssued = false;
		state.HasPatrolPoint = false;
		AssignNewPatrolPoint(state, player, immediate: true);
	}

	private static void ClearLoadedInstance(WardenState state)
	{
		state.Object = null;
		state.AI = null;
		state.Spawned = false;
		state.StuckTime = 0f;
		state.MovementCheckTimer = 0f;
		state.HasPatrolPoint = false;
		state.PatrolTimer = 0f;
		state.WasInCombat = false;
		state.SoftLeashIssued = false;
	}

	private void UpdateWardenMovement(WardenState state, Player player)
	{
		//IL_0050: 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_00a6: 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)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)state.Object == (Object)null || (Object)(object)state.AI == (Object)null || (Object)(object)player == (Object)null || _postInteriorTransferGraceTimer > 0f)
		{
			return;
		}
		bool flag = Character.InInterior(((Component)player).transform.position);
		bool flag2 = Character.InInterior(state.Object.transform.position);
		if (flag != flag2)
		{
			Vector3 returnPosition = GetReturnPosition(player, state.Slot, flag);
			TransferLoadedWarden(state, player, returnPosition);
			return;
		}
		float num = Vector3.Distance(state.Object.transform.position, ((Component)player).transform.position);
		if (num >= 70f)
		{
			CatchUpToPlayer(state, player, "more than 70 m away");
		}
		else
		{
			UpdatePeacefulPatrol(state, player, num);
		}
	}

	private void CatchUpToPlayer(WardenState state, Player player, string reason)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: 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)
		bool insideInterior = Character.InInterior(((Component)player).transform.position);
		Vector3 patrolPosition = GetPatrolPosition(player, state.Slot, insideInterior);
		TransferLoadedWarden(state, player, patrolPosition);
		Logger.LogInfo((object)(GetDisplayName(state.Slot) + " caught up because they were " + reason + "."));
	}

	private static Vector3 GetReturnPosition(Player player, int slot, bool insideInterior)
	{
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: 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_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: 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_004c: 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_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01be: 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_01c4: 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_00cd: 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_00d8: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: 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_01fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_020d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0212: Unknown result type (might be due to invalid IL or missing references)
		//IL_01df: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f8: 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_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: 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)
		//IL_017e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: 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_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: 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_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: 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)
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_0215: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: 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_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: 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)
		if (insideInterior)
		{
			return (Vector3)(slot switch
			{
				0 => ((Component)player).transform.position - ((Component)player).transform.forward * 1.5f - ((Component)player).transform.right * 0.9f, 
				1 => ((Component)player).transform.position - ((Component)player).transform.forward * 1.5f + ((Component)player).transform.right * 0.9f, 
				_ => ((Component)player).transform.position - ((Component)player).transform.forward * 2.5f, 
			}) + Vector3.up * 0.1f;
		}
		Vector3 val = (Vector3)(slot switch
		{
			0 => ((Component)player).transform.position - ((Component)player).transform.forward * 3f - ((Component)player).transform.right * 2.5f, 
			1 => ((Component)player).transform.position - ((Component)player).transform.forward * 3f + ((Component)player).transform.right * 2.5f, 
			_ => ((Component)player).transform.position - ((Component)player).transform.forward * 5f, 
		});
		Vector3 val2 = val + Vector3.up * 5f;
		RaycastHit val3 = default(RaycastHit);
		return (!Physics.Raycast(val2, Vector3.down, ref val3, 15f)) ? (val + Vector3.up * 0.5f) : (((RaycastHit)(ref val3)).point + Vector3.up * 0.2f);
	}

	internal static string GetDisplayName(int slot)
	{
		return slot switch
		{
			0 => "Bodvar", 
			1 => "Starkad", 
			2 => "Orvar", 
			_ => "Lyngvi Warden", 
		};
	}
}
internal class LyngviWardenMarker : MonoBehaviour
{
	private Character _character;

	private ZNetView _nview;

	private float _regenerationTimer;

	public int Slot { get; private set; } = -1;

	public WardenRole Role { get; private set; } = WardenRole.Tank;

	public float ElementalDamage { get; private set; } = 10f;

	private void Awake()
	{
		_character = ((Component)this).GetComponent<Character>();
		_nview = ((Component)this).GetComponent<ZNetView>();
		_regenerationTimer = 1f;
	}

	public void Configure(int slot, WardenRole role)
	{
		Slot = slot;
		Role = role;
		if ((Object)(object)_character == (Object)null)
		{
			_character = ((Component)this).GetComponent<Character>();
		}
		if ((Object)(object)_nview == (Object)null)
		{
			_nview = ((Component)this).GetComponent<ZNetView>();
		}
		if ((Object)(object)_nview != (Object)null && _nview.IsValid())
		{
			ZDO zDO = _nview.GetZDO();
			if (zDO != null)
			{
				ElementalDamage = zDO.GetFloat("SoulboundCompanion_ElementalDamage", ElementalDamage);
			}
		}
	}

	public void SetElementalDamage(float damage)
	{
		ElementalDamage = damage;
	}

	private void Update()
	{
		if ((Object)(object)_character == (Object)null || _character.IsDead() || ((Object)(object)_nview != (Object)null && _nview.IsValid() && !_nview.IsOwner()))
		{
			return;
		}
		_regenerationTimer -= Time.deltaTime;
		if (!(_regenerationTimer > 0f))
		{
			_regenerationTimer = 1f;
			float maxHealth = _character.GetMaxHealth();
			float health = _character.GetHealth();
			if (!(maxHealth <= 0f) && !(health <= 0f) && !(health >= maxHealth))
			{
				float num = maxHealth * 0.02f;
				_character.SetHealth(Mathf.Min(maxHealth, health + num));
			}
		}
	}
}