Decompiled source of Rideable Boars v1.0.4

BepInEx/plugins/RideableBoars/RideableBoars.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Splatform;
using UnityEngine;
using UnityEngine.UI;
using Valheim.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.4.0")]
namespace BoarRiding;

[BepInPlugin("xneliox.rideableboars", "Rideable Boars", "1.0.4")]
public sealed class BoarRidingPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "xneliox.rideableboars";

	public const string PluginName = "Rideable Boars";

	public const string PluginVersion = "1.0.4";

	internal static bool HeadlessServer;

	internal const string ItemPrefabName = "BoarSaddle";

	internal static BoarRidingPlugin Instance;

	internal static GameObject SaddleItemPrefab;

	internal static Recipe SaddleRecipe;

	internal static AssetBundle SaddleAssetBundle;

	internal static readonly GameObject[] CustomSaddleVisuals = (GameObject[])(object)new GameObject[3];

	private static readonly HashSet<int> WildBarebackRides = new HashSet<int>();

	internal ConfigEntry<float> OneStarSpeed;

	internal ConfigEntry<float> TwoStarSpeed;

	internal ConfigEntry<float> SaddleX;

	internal ConfigEntry<float> SaddleY;

	internal ConfigEntry<float> SaddleZ;

	internal ConfigEntry<float> SaddleScale;

	internal ConfigEntry<float> RiderX;

	internal ConfigEntry<float> RiderY;

	internal ConfigEntry<float> RiderZ;

	internal ConfigEntry<float> RiderForwardCorrection;

	internal ConfigEntry<float> RiderExtraForwardOffset;

	internal ConfigEntry<float> NormalBoarSaddleYOffset;

	internal ConfigEntry<float> NormalBoarSaddleScale;

	internal ConfigEntry<float> CustomSaddleSizeMultiplier;

	internal ConfigEntry<float> CustomSaddleZ;

	internal ConfigEntry<float> CustomSaddleZTwoStars;

	internal ConfigEntry<float> CustomSaddleYNoStars;

	internal ConfigEntry<float> CustomSaddleYOneStar;

	internal ConfigEntry<float> CustomSaddleYTwoStars;

	internal ConfigEntry<float> CustomSaddlePitchOneStar;

	internal ConfigEntry<bool> AllowMountedPortalTravel;

	internal ConfigEntry<bool> MovementKeySteering;

	internal ConfigEntry<float> MountedCameraShakeMultiplier;

	internal ConfigEntry<KeyboardShortcut> CommandWheelShortcut;

	internal ConfigEntry<KeyboardShortcut> MountShortcut;

	internal ConfigEntry<KeyboardShortcut> DismountShortcut;

	internal ConfigEntry<KeyboardShortcut> BarebackShortcut;

	internal ConfigEntry<KeyboardShortcut> RemoveSaddleShortcut;

	internal ConfigEntry<float> RecallRange;

	internal ConfigEntry<float> FollowRange;

	internal ConfigEntry<float> RidingSkillStaminaRegenPerLevel;

	internal ConfigEntry<bool> BoarLevelSystemEnabled;

	internal ConfigEntry<int> BoarMaximumLevel;

	internal ConfigEntry<float> BoarHealthPerLevel;

	internal ConfigEntry<float> OneStarBoarHealthPerLevel;

	internal ConfigEntry<float> TwoStarBoarHealthPerLevel;

	internal ConfigEntry<float> BoarHealthIncreaseEveryFiveLevels;

	internal ConfigEntry<float> WildBoarNextLevelChance;

	internal ConfigEntry<float> BoarBaseExperience;

	internal ConfigEntry<float> BoarExperienceGrowth;

	internal ConfigEntry<float> BoarRidingDistanceExperience;

	internal ConfigEntry<float> BoarRidingTimeExperience;

	internal ConfigEntry<float> BoarCommandExperience;

	internal ConfigEntry<float> BoarCombatExperienceMultiplier;

	internal ConfigEntry<float> BoarKillExperience;

	internal ConfigEntry<float> BoarSurvivalExperienceMultiplier;

	internal ConfigEntry<float> BoarExperienceRate;

	internal ConfigEntry<float> BoarCatchUpMultiplier;

	internal ConfigEntry<float> FavouriteHealthRegeneration;

	internal ConfigEntry<bool> FavouriteRegenerationInCombat;

	internal ConfigEntry<bool> ShowBoarLevel;

	internal ConfigEntry<bool> ShowLevelUpEffect;

	private ZDOID _lastRiddenBoar = ZDOID.None;

	private ZDOID _lastRiddenAnimal = ZDOID.None;

	private ZDOID _favouritePinAnimal = ZDOID.None;

	private string _favouriteAnimalName = string.Empty;

	private bool _favouriteAnimalIsBoar;

	private bool _favouriteAnimalDead;

	private Vector3 _favouriteLastKnownPosition;

	private bool _hasFavouriteLastKnownPosition;

	private PinData _favouriteAnimalPin;

	private Minimap _favouritePinMap;

	private Sprite _favouriteBoarIcon;

	private float _nextFavouritePinUpdate;

	private float _nextFavouriteHeal;

	private float _nextFavouritePositionSave;

	private string _favouritePreferenceKey;

	private string _favouriteFallbackPreferenceKey;

	private bool _favouriteServerStateSynchronized;

	private long _favouriteSavedAtUtcTicks;

	private long _favouriteWorldUid;

	private bool _favouritePinMismatchReported;

	private ZRoutedRpc _registeredKickRpc;

	internal BoarCommandRadialConfig _commandRadial;

	private bool _commandWheelOpenRequested;

	private bool _commandAllTamedBoars;

	private ZDOID _progressionRiddenBoar = ZDOID.None;

	private Vector3 _progressionLastPosition;

	private float _progressionDistance;

	private float _progressionRideTime;

	private readonly Dictionary<ZDOID, float> _nextCommandExperience = new Dictionary<ZDOID, float>();

	private static readonly FieldRef<Hud, RadialBase> RadialMenu = AccessTools.FieldRefAccess<Hud, RadialBase>("m_radialMenu");

	private Harmony _harmony;

	private const string KickRiderRpc = "RideableBoars_KickRider";

	private static readonly int FavouriteOwnerKey = StringExtensionMethods.GetStableHashCode("RideableBoars_FavouriteOwner");

	private static readonly int FavouriteMountStateKey = StringExtensionMethods.GetStableHashCode("RideableBoars_FavouriteMountState");

	internal static readonly int BoarLevelKey = StringExtensionMethods.GetStableHashCode("RideableBoars_Level");

	internal static readonly int BoarExperienceKey = StringExtensionMethods.GetStableHashCode("RideableBoars_Experience");

	internal static readonly int BoarBaseHealthKey = StringExtensionMethods.GetStableHashCode("RideableBoars_BaseHealth");

	internal static void RegisterLocalization()
	{
		Localization instance = Localization.instance;
		if (instance != null)
		{
			RegisterLocalization(instance, instance.GetSelectedLanguage());
		}
	}

	internal static void RegisterLocalization(Localization localization, string language)
	{
		if (localization != null)
		{
			bool flag = string.Equals(language, "Russian", StringComparison.OrdinalIgnoreCase) || string.Equals(language, "Русский", StringComparison.OrdinalIgnoreCase);
			bool flag2 = string.Equals(language, "German", StringComparison.OrdinalIgnoreCase) || string.Equals(language, "Deutsch", StringComparison.OrdinalIgnoreCase);
			AddLocalizedWord(localization, "boarriding_saddle_name", flag ? "Седло для кабана" : (flag2 ? "Wildschweinsattel" : "Boar Saddle"));
			AddLocalizedWord(localization, "boarriding_saddle_description", flag ? "Прочное седло для верховой езды на приручённых кабанах." : (flag2 ? "Ein robuster Sattel zum Reiten gezähmter Wildschweine." : "A sturdy saddle for riding tamed boars."));
			AddLocalizedWord(localization, "boarriding_ride_bareback", flag ? "Ехать без седла" : (flag2 ? "Ohne Sattel reiten" : "Ride bareback"));
			AddLocalizedWord(localization, "boarriding_command_wheel", flag ? "Команды кабану" : (flag2 ? "Wildschweinbefehle" : "Boar Commands"));
			AddLocalizedWord(localization, "boarriding_follow", flag ? "Следовать" : (flag2 ? "Folgen" : "Follow"));
			AddLocalizedWord(localization, "boarriding_wait", flag ? "Ждать" : (flag2 ? "Warten" : "Wait"));
			AddLocalizedWord(localization, "boarriding_recall", flag ? "Позвать" : (flag2 ? "Rückruf" : "Recall"));
			AddLocalizedWord(localization, "boarriding_recalled", flag ? "Кабан был отозван." : (flag2 ? "Das Wildschwein wurde zurückgerufen." : "The boar was recalled."));
			AddLocalizedWord(localization, "boarriding_roam", flag ? "Свободно двигаться" : (flag2 ? "Freies Bewegen" : "Roam freely"));
			AddLocalizedWord(localization, "boarriding_roaming", flag ? "Кабан снова свободно двигается" : (flag2 ? "Das Wildschwein bewegt sich wieder frei" : "The boar is roaming freely again"));
			AddLocalizedWord(localization, "boarriding_follow_too_far", flag ? "Кабан слишком далеко, чтобы следовать" : (flag2 ? "Das Wildschwein ist zu weit entfernt, um dir zu folgen" : "The boar is too far away to follow"));
			AddLocalizedWord(localization, "boarriding_target_favourite", flag ? "Только избранный кабан" : (flag2 ? "Nur Lieblingswildschwein" : "Favourite boar only"));
			AddLocalizedWord(localization, "boarriding_target_all", flag ? "Все приручённые кабаны" : (flag2 ? "Alle gezähmten Wildschweine" : "All tamed boars"));
			AddLocalizedWord(localization, "boarriding_scope_favourite", flag ? "Команды: избранный кабан" : (flag2 ? "Befehle gelten jetzt nur für das Lieblingswildschwein" : "Commands now target the favourite boar only"));
			AddLocalizedWord(localization, "boarriding_scope_all", flag ? "Команды: все приручённые кабаны" : (flag2 ? "Befehle gelten jetzt für alle geladenen gezähmten Wildschweine" : "Commands now target all loaded tamed boars"));
			AddLocalizedWord(localization, "boarriding_no_loaded_boars", flag ? "Нет загруженных приручённых кабанов" : (flag2 ? "Keine geladenen gezähmten Wildschweine gefunden" : "No loaded tamed boars found"));
			AddLocalizedWord(localization, "boarriding_no_boar", flag ? "Нет доступного приручённого кабана." : (flag2 ? "Kein verfügbares gezähmtes Wildschwein." : "No available tamed boar."));
			AddLocalizedWord(localization, "boarriding_out_of_range", flag ? "Кабан слишком далеко." : (flag2 ? "Das Wildschwein ist zu weit entfernt." : "The boar is too far away."));
			AddLocalizedWord(localization, "boarriding_waiting", flag ? "Кабан ждёт." : (flag2 ? "Das Wildschwein wartet." : "The boar is waiting."));
			AddLocalizedWord(localization, "boarriding_following", flag ? "Кабан следует за вами." : (flag2 ? "Das Wildschwein folgt dir." : "The boar is following you."));
			AddLocalizedWord(localization, "boarriding_favourite_boar", flag ? "Любимый кабан" : (flag2 ? "Lieblingswildschwein" : "Favourite Boar"));
			AddLocalizedWord(localization, "boarriding_favourite_mount", flag ? "Любимое ездовое животное" : (flag2 ? "Lieblingsreittier" : "Favourite Mount"));
			AddLocalizedWord(localization, "boarriding_favourite_boar_dead", flag ? "Любимый кабан погиб" : (flag2 ? "Lieblingswildschwein gestorben" : "Favourite Boar died"));
			AddLocalizedWord(localization, "boarriding_favourite_dead_command", flag ? "Ваш любимый кабан погиб." : (flag2 ? "Dein Lieblingswildschwein ist gestorben." : "Your favourite boar has died."));
			AddLocalizedWord(localization, "boarriding_eject_rider", flag ? "Сбросить чужого всадника" : (flag2 ? "Fremden Reiter abwerfen" : "Eject other rider"));
			AddLocalizedWord(localization, "boarriding_not_owner", flag ? "Этот кабан вам не принадлежит." : (flag2 ? "Dieses Wildschwein gehört dir nicht." : "This boar does not belong to you."));
			AddLocalizedWord(localization, "boarriding_no_other_rider", flag ? "На вашем кабане нет чужого всадника." : (flag2 ? "Auf deinem Wildschwein sitzt kein fremder Reiter." : "No other rider is using your boar."));
			AddLocalizedWord(localization, "boarriding_eject_sent", flag ? "Чужой всадник сброшен." : (flag2 ? "Der fremde Reiter wird abgeworfen." : "The other rider is being ejected."));
			AddLocalizedWord(localization, "boarriding_you_were_ejected", flag ? "Владелец сбросил вас с кабана." : (flag2 ? "Der Besitzer hat dich vom Wildschwein abgeworfen." : "The owner ejected you from the boar."));
			AddLocalizedWord(localization, "boarriding_level", flag ? "Уровень" : (flag2 ? "Level" : "Level"));
			AddLocalizedWord(localization, "boarriding_level_up", flag ? "достиг уровня" : (flag2 ? "hat Level erreicht" : "reached Level"));
			AddLocalizedWord(localization, "boarriding_level_up_message", flag ? "{0} - достиг уровня {1}" : (flag2 ? "{0} - hat Level {1} erreicht" : "{0} - reached Level {1}"));
		}
	}

	private static void AddLocalizedWord(Localization localization, string key, string text)
	{
		AccessTools.Method(typeof(Localization), "AddWord", (Type[])null, (Type[])null).Invoke(localization, new object[2] { key, text });
	}

	private unsafe void Awake()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Invalid comparison between Unknown and I4
		//IL_0374: Unknown result type (might be due to invalid IL or missing references)
		//IL_037e: Expected O, but got Unknown
		//IL_039b: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_041f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0454: Unknown result type (might be due to invalid IL or missing references)
		//IL_0497: Unknown result type (might be due to invalid IL or missing references)
		//IL_04a1: Expected O, but got Unknown
		//IL_04d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_04df: Expected O, but got Unknown
		//IL_0513: Unknown result type (might be due to invalid IL or missing references)
		//IL_051d: Expected O, but got Unknown
		//IL_056b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0575: Expected O, but got Unknown
		//IL_05a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_05b3: Expected O, but got Unknown
		//IL_05e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_05f1: Expected O, but got Unknown
		//IL_0625: Unknown result type (might be due to invalid IL or missing references)
		//IL_062f: Expected O, but got Unknown
		//IL_0663: Unknown result type (might be due to invalid IL or missing references)
		//IL_066d: Expected O, but got Unknown
		//IL_06a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_06ab: Expected O, but got Unknown
		//IL_06df: Unknown result type (might be due to invalid IL or missing references)
		//IL_06e9: Expected O, but got Unknown
		//IL_071d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0727: Expected O, but got Unknown
		//IL_075b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0765: Expected O, but got Unknown
		//IL_0799: Unknown result type (might be due to invalid IL or missing references)
		//IL_07a3: Expected O, but got Unknown
		//IL_07d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_07e1: Expected O, but got Unknown
		//IL_0815: Unknown result type (might be due to invalid IL or missing references)
		//IL_081f: Expected O, but got Unknown
		//IL_0853: Unknown result type (might be due to invalid IL or missing references)
		//IL_085d: Expected O, but got Unknown
		//IL_0891: Unknown result type (might be due to invalid IL or missing references)
		//IL_089b: Expected O, but got Unknown
		//IL_08cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_08d9: Expected O, but got Unknown
		//IL_090d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0917: Expected O, but got Unknown
		//IL_094b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0955: Expected O, but got Unknown
		//IL_0ab3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ab8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0abb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ac2: Invalid comparison between Unknown and I4
		//IL_0b3a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b3f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b42: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b49: Invalid comparison between Unknown and I4
		//IL_0b54: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b59: Unknown result type (might be due to invalid IL or missing references)
		//IL_0aca: Unknown result type (might be due to invalid IL or missing references)
		//IL_0acf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b1f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0af1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0af6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b6a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b6f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b76: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b7d: Invalid comparison between Unknown and I4
		//IL_0ba9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0e96: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ea0: Expected O, but got Unknown
		Instance = this;
		HeadlessServer = (int)SystemInfo.graphicsDeviceType == 4;
		MigrateLegacyConfig();
		LoadCustomSaddleAssets();
		OneStarSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "One Star", 1.2f, "Speed multiplier for a one-star boar.");
		TwoStarSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "Two Stars", 1.4f, "Speed multiplier for a two-star boar.");
		SaddleX = ((BaseUnityPlugin)this).Config.Bind<float>("Saddle Position", "X", 0f, "Horizontal saddle position.");
		SaddleY = ((BaseUnityPlugin)this).Config.Bind<float>("Saddle Position", "Y", 1.02f, "Saddle height.");
		SaddleZ = ((BaseUnityPlugin)this).Config.Bind<float>("Saddle Position", "Z", 0.35f, "Forward/backward saddle position.");
		SaddleScale = ((BaseUnityPlugin)this).Config.Bind<float>("Saddle Position", "Scale", 0.36f, "Scale of the visible saddle.");
		RiderX = ((BaseUnityPlugin)this).Config.Bind<float>("Rider Position", "X", 0f, "Horizontal rider position.");
		RiderY = ((BaseUnityPlugin)this).Config.Bind<float>("Rider Position", "Y", 0.25f, "Rider height relative to the saddle.");
		RiderZ = ((BaseUnityPlugin)this).Config.Bind<float>("Rider Position", "Z", 0.25f, "Forward/backward rider position.");
		RiderForwardCorrection = ((BaseUnityPlugin)this).Config.Bind<float>("Rider Position", "Custom Saddle Forward Correction", 0.32f, "Moves the rider toward the boar's head so the character sits in the custom saddle centre.");
		RiderExtraForwardOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Rider Position", "Extra Forward Offset", 0.12f, "Additional forward rider offset for all star levels, with or without a saddle. Does not move the saddle or interaction area.");
		NormalBoarSaddleYOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Saddle Position", "No-Star Boar Height Offset", -0.16f, "Additional height correction for no-star boars only.");
		NormalBoarSaddleScale = ((BaseUnityPlugin)this).Config.Bind<float>("Saddle Position", "No-Star Boar Scale", 0.84f, "Additional scale multiplier for no-star boars only.");
		CustomSaddleSizeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Saddle Position", "Custom Model Scale", 0.68f, "Shared size multiplier for the custom saddle models.");
		CustomSaddleZ = ((BaseUnityPlugin)this).Config.Bind<float>("Custom Saddle Fit", "Back Position", -0.05f, "Forward/backward position of the custom saddle models.");
		CustomSaddleZTwoStars = ((BaseUnityPlugin)this).Config.Bind<float>("Custom Saddle Fit", "Two-Star Back Position", -0.05f, "Forward/backward position of the custom saddle on two-star boars, centred farther back on the large upper back.");
		CustomSaddleYNoStars = ((BaseUnityPlugin)this).Config.Bind<float>("Custom Saddle Fit", "No-Star Height", 0.95f, "Back height of the custom saddle on a no-star boar.");
		CustomSaddleYOneStar = ((BaseUnityPlugin)this).Config.Bind<float>("Custom Saddle Fit", "One-Star Height", 1.015f, "Back height of the custom saddle on a one-star boar.");
		CustomSaddleYTwoStars = ((BaseUnityPlugin)this).Config.Bind<float>("Custom Saddle Fit", "Two-Star Height", 1.11f, "Back height of the custom saddle on a two-star boar.");
		CustomSaddlePitchOneStar = ((BaseUnityPlugin)this).Config.Bind<float>("Custom Saddle Fit", "One-Star Back Pitch", 0.75f, "Tilts the one-star saddle to follow the rising shoulder line without clipping through the back.");
		AllowMountedPortalTravel = ((BaseUnityPlugin)this).Config.Bind<bool>("Portal Travel", "Allow Mounted Boars", true, "Allows a player who is riding a boar to take the boar through a portal.");
		MovementKeySteering = ((BaseUnityPlugin)this).Config.Bind<bool>("Riding Camera", "Movement-Key Steering", true, "Steers a ridden boar with the movement keys instead of the camera direction. This allows free camera movement while riding.");
		MountedCameraShakeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Riding Camera", "Camera Shake Multiplier", 0f, new ConfigDescription("Camera-shake strength while riding a boar. 0 disables it, 0.5 halves it and 1 keeps Valheim's normal strength.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		CommandWheelShortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "Open Command Wheel", new KeyboardShortcut((KeyCode)98, Array.Empty<KeyCode>()), "Opens the command wheel for the last ridden tamed boar.");
		MountShortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "Mount Saddled Boar", new KeyboardShortcut((KeyCode)101, Array.Empty<KeyCode>()), "Mounts the targeted saddled boar. E keeps Valheim's normal interaction behavior.");
		DismountShortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "Dismount", new KeyboardShortcut((KeyCode)101, Array.Empty<KeyCode>()), "Dismounts with E when no usable object is targeted. When an object is targeted, E interacts without dismounting.");
		BarebackShortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "Ride Bareback", new KeyboardShortcut((KeyCode)101, Array.Empty<KeyCode>()), "Rides an unsaddled boar only while aiming at its back.");
		RemoveSaddleShortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "Remove Saddle", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Removes and recovers the targeted boar saddle.");
		RecallRange = ((BaseUnityPlugin)this).Config.Bind<float>("Commands", "Recall Range", 200f, new ConfigDescription("Maximum distance at which the last ridden boar can hear the recall command.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 1000f), Array.Empty<object>()));
		FollowRange = ((BaseUnityPlugin)this).Config.Bind<float>("Commands", "Follow Range", 15f, new ConfigDescription("Maximum distance for the normal follow command. Use Recall for greater distances.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 100f), Array.Empty<object>()));
		RidingSkillStaminaRegenPerLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Riding", "Stamina Regeneration Per Skill Level", 0.05f, new ConfigDescription("Additional mount stamina regenerated per second for every Riding skill level while actively riding. At level 100 the default adds 5 stamina per second.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>()));
		BoarLevelSystemEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Boar Levels", "Enabled", true, "Enables persistent levels and experience for tamed boars.");
		BoarMaximumLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Boar Levels", "Maximum Level", 50, new ConfigDescription("Highest level a tamed boar can reach.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 200), Array.Empty<object>()));
		BoarHealthPerLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Health Per Level", 1f, new ConfigDescription("Base maximum-health gain per progression level for a normal boar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		OneStarBoarHealthPerLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "1-Star Health Per Level", 2f, new ConfigDescription("Base maximum-health gain per progression level for a one-star boar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		TwoStarBoarHealthPerLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "2-Star Health Per Level", 3f, new ConfigDescription("Base maximum-health gain per progression level for a two-star boar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		BoarHealthIncreaseEveryFiveLevels = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Health Gain Increase Every 5 Levels", 0.5f, new ConfigDescription("Additional health added to every later level-up after each five-level band, independently for every star tier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		WildBoarNextLevelChance = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Wild Boar Next Level Chance", 0.45f, new ConfigDescription("Chance for a newly spawned wild boar to advance one more progression level. Every level is possible, but higher levels become increasingly rare.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 0.95f), Array.Empty<object>()));
		BoarBaseExperience = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Base Experience Required", 25f, new ConfigDescription("Experience required for level 2.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10000f), Array.Empty<object>()));
		BoarExperienceGrowth = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Experience Growth", 1.1f, new ConfigDescription("Experience requirement multiplier for each later level.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f), Array.Empty<object>()));
		BoarRidingDistanceExperience = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Experience Per 25 Metres Ridden", 4f, new ConfigDescription("Experience awarded for every 25 metres actually travelled while riding.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		BoarRidingTimeExperience = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Experience Per Minute Ridden", 5f, new ConfigDescription("Experience awarded for every active minute of riding.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		BoarCommandExperience = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Experience Per Command", 1f, new ConfigDescription("Experience for an executed movement command. Each boar has a cooldown to prevent farming.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		BoarCombatExperienceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Combat Experience Multiplier", 0.1f, new ConfigDescription("Experience per point of damage dealt by the boar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
		BoarKillExperience = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Kill Experience", 20f, new ConfigDescription("Base experience for an enemy killed by the boar, multiplied by the enemy's star level.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f), Array.Empty<object>()));
		BoarSurvivalExperienceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Survival Experience Multiplier", 0.02f, new ConfigDescription("Experience per point of damage survived by a tamed boar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		BoarExperienceRate = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "Experience Rate", 1.25f, new ConfigDescription("Global boar experience multiplier. The default makes boars level slightly faster than the passive Riding skill.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
		BoarCatchUpMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Boar Levels", "New Mount Catch-Up Bonus", 2f, new ConfigDescription("Maximum additional experience multiplier for a boar below the rider's comparable progression. This speeds up new mounts without copying the Riding skill level.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
		FavouriteHealthRegeneration = ((BaseUnityPlugin)this).Config.Bind<float>("Favourite Boar", "Health Regeneration Per Second", 1f, new ConfigDescription("Passive health regenerated each second by the current favourite boar.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		FavouriteRegenerationInCombat = ((BaseUnityPlugin)this).Config.Bind<bool>("Favourite Boar", "Regenerate During Combat", false, "Allows favourite-boar regeneration while it has an active target or is attacking.");
		ShowBoarLevel = ((BaseUnityPlugin)this).Config.Bind<bool>("Boar Levels", "Show Level", true, "Shows the persistent level next to a tamed boar's name.");
		ShowLevelUpEffect = ((BaseUnityPlugin)this).Config.Bind<bool>("Boar Levels", "Show Level Up Effect", true, "Shows a message and a subtle effect when a boar levels up.");
		if (Mathf.Approximately(BoarBaseExperience.Value, 100f))
		{
			BoarBaseExperience.Value = 25f;
		}
		if (Mathf.Approximately(BoarExperienceGrowth.Value, 1.2f))
		{
			BoarExperienceGrowth.Value = 1.1f;
		}
		if (Mathf.Approximately(BoarRidingDistanceExperience.Value, 2f))
		{
			BoarRidingDistanceExperience.Value = 4f;
		}
		if (Mathf.Approximately(BoarRidingTimeExperience.Value, 2f))
		{
			BoarRidingTimeExperience.Value = 5f;
		}
		if (BoarHealthPerLevel.Value <= 0.25f)
		{
			BoarHealthPerLevel.Value = 1f;
		}
		if (Mathf.Approximately(RecallRange.Value, 60f))
		{
			RecallRange.Value = 200f;
			((BaseUnityPlugin)this).Config.Save();
		}
		KeyboardShortcut value = BarebackShortcut.Value;
		if ((int)((KeyboardShortcut)(ref value)).MainKey == 101)
		{
			value = BarebackShortcut.Value;
			if (((object)(*(KeyboardShortcut*)(&value))/*cast due to .constrained prefix*/).ToString().IndexOf("Shift", StringComparison.OrdinalIgnoreCase) < 0)
			{
				value = BarebackShortcut.Value;
				if (((object)(*(KeyboardShortcut*)(&value))/*cast due to .constrained prefix*/).ToString().IndexOf("Control", StringComparison.OrdinalIgnoreCase) < 0)
				{
					goto IL_0b34;
				}
			}
			BarebackShortcut.Value = new KeyboardShortcut((KeyCode)101, Array.Empty<KeyCode>());
			((BaseUnityPlugin)this).Config.Save();
		}
		goto IL_0b34;
		IL_0b34:
		value = DismountShortcut.Value;
		bool flag = (int)((KeyboardShortcut)(ref value)).MainKey == 101;
		int num = 0;
		value = DismountShortcut.Value;
		foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers)
		{
			num++;
			flag = flag && (int)modifier == 306;
		}
		if (flag && num == 1)
		{
			DismountShortcut.Value = new KeyboardShortcut((KeyCode)101, Array.Empty<KeyCode>());
			((BaseUnityPlugin)this).Config.Save();
		}
		if (Mathf.Approximately(CustomSaddleZTwoStars.Value, 0.2f))
		{
			CustomSaddleZTwoStars.Value = -0.05f;
			((BaseUnityPlugin)this).Config.Save();
		}
		if (Mathf.Approximately(SaddleY.Value, 0.68f) && Mathf.Approximately(SaddleZ.Value, -0.08f) && Mathf.Approximately(SaddleScale.Value, 0.55f))
		{
			SaddleY.Value = 0.9f;
			SaddleZ.Value = 0.1f;
			SaddleScale.Value = 0.42f;
			((BaseUnityPlugin)this).Config.Save();
		}
		if (Mathf.Approximately(SaddleY.Value, 0.9f) && Mathf.Approximately(SaddleZ.Value, 0.1f) && Mathf.Approximately(SaddleScale.Value, 0.42f))
		{
			SaddleY.Value = 0.84f;
			SaddleZ.Value = -0.35f;
			SaddleScale.Value = 0.32f;
			((BaseUnityPlugin)this).Config.Save();
		}
		if (Mathf.Approximately(SaddleY.Value, 0.84f) && Mathf.Approximately(SaddleZ.Value, -0.35f) && Mathf.Approximately(SaddleScale.Value, 0.32f))
		{
			SaddleY.Value = 1.02f;
			SaddleZ.Value = 0.35f;
			SaddleScale.Value = 0.36f;
			if (Mathf.Approximately(RiderZ.Value, 0f))
			{
				RiderZ.Value = 0.25f;
			}
			((BaseUnityPlugin)this).Config.Save();
		}
		if (Mathf.Approximately(CustomSaddleYNoStars.Value, 0.9f))
		{
			CustomSaddleYNoStars.Value = 0.95f;
			((BaseUnityPlugin)this).Config.Save();
		}
		if (Mathf.Approximately(CustomSaddleYOneStar.Value, 1f) || Mathf.Approximately(CustomSaddleYOneStar.Value, 1.03f))
		{
			CustomSaddleYOneStar.Value = 1.015f;
			((BaseUnityPlugin)this).Config.Save();
		}
		if (Mathf.Approximately(CustomSaddlePitchOneStar.Value, -3f) || Mathf.Approximately(CustomSaddlePitchOneStar.Value, -0.5f))
		{
			CustomSaddlePitchOneStar.Value = 0.75f;
			((BaseUnityPlugin)this).Config.Save();
		}
		if (Mathf.Approximately(RiderForwardCorrection.Value, 0.22f))
		{
			RiderForwardCorrection.Value = 0.32f;
			((BaseUnityPlugin)this).Config.Save();
		}
		_harmony = new Harmony("xneliox.rideableboars");
		_harmony.PatchAll(typeof(BoarRidingPlugin).Assembly);
		if (!HeadlessServer)
		{
			_commandRadial = ScriptableObject.CreateInstance<BoarCommandRadialConfig>();
			_commandRadial.PreloadIcons();
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Rideable Boars 1.0.4 loaded.");
	}

	private void MigrateLegacyConfig()
	{
		try
		{
			string text = Path.Combine(Paths.ConfigPath, "valheim.boarriding.cfg");
			string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
			if (File.Exists(text) && !File.Exists(configFilePath))
			{
				File.Copy(text, configFilePath, overwrite: false);
				((BaseUnityPlugin)this).Config.Reload();
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Migrated the legacy configuration to " + Path.GetFileName(configFilePath) + "."));
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not migrate the legacy configuration: " + ex.Message));
		}
	}

	private void Update()
	{
		//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_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		if (HeadlessServer || (Object)(object)Player.m_localPlayer == (Object)null)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		EnsureKickRpcRegistered();
		EnsureFavouriteLoaded(localPlayer);
		IDoodadController doodadController = localPlayer.GetDoodadController();
		Sadle val = (Sadle)(object)((doodadController is Sadle) ? doodadController : null);
		Character val2 = (Object.op_Implicit((Object)(object)val) ? val.GetCharacter() : null);
		if (Object.op_Implicit((Object)(object)val2))
		{
			RememberFavourite(val2, localPlayer);
		}
		UpdateRidingProgression(val2);
		UpdateFavouriteAnimalPin();
		UpdateFavouriteHealthRegeneration(localPlayer);
		KeyboardShortcut value = DismountShortcut.Value;
		if (((KeyboardShortcut)(ref value)).IsDown() && Object.op_Implicit((Object)(object)val) && IsBoar(val.GetCharacter()) && !IsPlainUseShortcut(DismountShortcut.Value))
		{
			val.OnUseStop(localPlayer);
			return;
		}
		RadialBase val3 = (Object.op_Implicit((Object)(object)Hud.instance) ? RadialMenu.Invoke(Hud.instance) : null);
		if (_commandWheelOpenRequested && Object.op_Implicit((Object)(object)val3) && !val3.Active)
		{
			OpenCommandWheel(val3);
			if (val3.Active && (object)val3.CurrentConfig == _commandRadial)
			{
				_commandWheelOpenRequested = false;
			}
		}
		int num;
		if (Object.op_Implicit((Object)(object)val3) && val3.Active)
		{
			num = (((object)val3.CurrentConfig == _commandRadial) ? 1 : 0);
			if (num != 0)
			{
				ZCursor.LockState = (CursorLockMode)0;
				ZCursor.Show();
				_commandRadial.RefreshCenterText(val3);
			}
		}
		else
		{
			num = 0;
		}
		if (num != 0 && Input.GetKeyDown((KeyCode)27))
		{
			CloseCommandWheel(val3);
			return;
		}
		if (!IsTextEntryActive() && IsShortcutDownAllowExtraModifiers(CommandWheelShortcut.Value))
		{
			if (Object.op_Implicit((Object)(object)val3) && val3.Active && (object)val3.CurrentConfig == _commandRadial)
			{
				CloseCommandWheel(val3);
				_commandWheelOpenRequested = false;
			}
			else
			{
				_commandWheelOpenRequested = true;
				if (Object.op_Implicit((Object)(object)val3))
				{
					OpenCommandWheel(val3);
					if (val3.Active && (object)val3.CurrentConfig == _commandRadial)
					{
						_commandWheelOpenRequested = false;
					}
				}
			}
		}
		HandleCustomInteractionShortcuts(localPlayer, val);
	}

	private static bool IsShortcutDownAllowExtraModifiers(KeyboardShortcut shortcut)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if (!Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey))
		{
			return false;
		}
		foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
		{
			if (!Input.GetKey(modifier))
			{
				return false;
			}
		}
		return true;
	}

	private static bool IsPlainUseShortcut(KeyboardShortcut shortcut)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Invalid comparison between Unknown and I4
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 101)
		{
			return false;
		}
		using (IEnumerator<KeyCode> enumerator = ((KeyboardShortcut)(ref shortcut)).Modifiers.GetEnumerator())
		{
			if (enumerator.MoveNext())
			{
				_ = enumerator.Current;
				return false;
			}
		}
		return true;
	}

	private static bool IsTextEntryActive()
	{
		if (!TextInput.IsVisible() && !Console.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Minimap.InTextInput())
		{
			if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_buildUi != (Object)null)
			{
				return Hud.instance.m_buildUi.SearchFieldFocused;
			}
			return false;
		}
		return true;
	}

	private void HandleCustomInteractionShortcuts(Player player, Sadle ridden)
	{
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: 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_00a8: 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_00b3: Invalid comparison between Unknown and I4
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)ridden))
		{
			return;
		}
		GameObject hoverObject = ((Humanoid)player).GetHoverObject();
		if (!Object.op_Implicit((Object)(object)hoverObject))
		{
			return;
		}
		Tameable componentInParent = hoverObject.GetComponentInParent<Tameable>();
		if (!Object.op_Implicit((Object)(object)componentInParent) || !IsBoar(((Component)componentInParent).GetComponent<Character>()))
		{
			return;
		}
		Sadle componentInParent2 = hoverObject.GetComponentInParent<Sadle>();
		bool flag = Object.op_Implicit((Object)(object)componentInParent2) && Object.op_Implicit((Object)(object)componentInParent.m_saddle) && (Object)(object)componentInParent2 == (Object)(object)componentInParent.m_saddle;
		KeyboardShortcut value = RemoveSaddleShortcut.Value;
		if (((KeyboardShortcut)(ref value)).IsDown() && flag && componentInParent.IsTamed() && HasSaddle(componentInParent))
		{
			componentInParent.DropSaddle(((Component)player).transform.position);
			return;
		}
		KeyboardShortcut value2 = MountShortcut.Value;
		if ((int)((KeyboardShortcut)(ref value2)).MainKey != 101 && ((KeyboardShortcut)(ref value2)).IsDown() && HasSaddle(componentInParent) && Object.op_Implicit((Object)(object)componentInParent.m_saddle))
		{
			componentInParent.m_saddle.Interact((Humanoid)(object)player, false, false);
		}
	}

	private void OpenCommandWheel(RadialBase radial)
	{
		if (!Object.op_Implicit((Object)(object)radial) || (Object)(object)_commandRadial == (Object)null)
		{
			return;
		}
		radial.CanOpen = true;
		ZCursor.LockState = (CursorLockMode)0;
		ZCursor.Show();
		object value = AccessTools.Field(typeof(RadialBase), "m_animationManager").GetValue(radial);
		if (value != null)
		{
			MethodInfo methodInfo = AccessTools.Method(value.GetType(), "EndAll", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				methodInfo.Invoke(value, null);
			}
		}
		AccessTools.Field(typeof(RadialBase), "m_selected").SetValue(radial, null);
		radial.Open((IRadialConfig)(object)_commandRadial, (IRadialConfig)null);
	}

	internal bool IsOwnCommandWheel(RadialBase radial)
	{
		if (Object.op_Implicit((Object)(object)radial) && radial.Active && (Object)(object)_commandRadial != (Object)null)
		{
			return (object)radial.CurrentConfig == _commandRadial;
		}
		return false;
	}

	internal void RestoreCommandWheelCenter(RadialBase radial)
	{
		if (IsOwnCommandWheel(radial))
		{
			_commandRadial.RefreshCenterText(radial);
		}
	}

	internal void AddCommandWheelToMainRadial(RadialBase radial, List<RadialMenuElement> elements)
	{
		if (Object.op_Implicit((Object)(object)radial) && !((Object)(object)_commandRadial == (Object)null) && elements != null)
		{
			_commandRadial.RemoveCommandElements(elements);
			GroupElement val = Object.Instantiate<GroupElement>(RadialData.SO.GroupElement);
			val.Init((IRadialConfig)(object)_commandRadial, radial.CurrentConfig, radial);
			elements.Add((RadialMenuElement)(object)val);
		}
	}

	private void EnsureFavouriteLoaded(Player player)
	{
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)player) || (Object)(object)ZNet.instance == (Object)null)
		{
			return;
		}
		long worldUID = ZNet.instance.GetWorldUID();
		string text = "RideableBoars.Favourite." + worldUID + "." + player.GetPlayerID();
		string text2 = Convert.ToBase64String(Encoding.UTF8.GetBytes(player.GetPlayerName() ?? string.Empty));
		string text3 = "RideableBoars.Favourite.ByName." + worldUID + "." + text2;
		if (!string.Equals(_favouritePreferenceKey, text, StringComparison.Ordinal))
		{
			_favouritePreferenceKey = text;
			_favouriteFallbackPreferenceKey = text3;
			_favouriteServerStateSynchronized = false;
			_lastRiddenAnimal = ZDOID.None;
			_lastRiddenBoar = ZDOID.None;
			_favouriteAnimalName = string.Empty;
			_favouriteAnimalIsBoar = false;
			_favouriteAnimalDead = false;
			_favouriteLastKnownPosition = Vector3.zero;
			_hasFavouriteLastKnownPosition = false;
			_favouriteSavedAtUtcTicks = 0L;
			_favouriteWorldUid = worldUID;
			_favouritePinMismatchReported = false;
		}
		if (_favouriteServerStateSynchronized)
		{
			return;
		}
		ZNetView component = ((Component)player).GetComponent<ZNetView>();
		if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
		{
			return;
		}
		string text4 = component.GetZDO().GetString(FavouriteMountStateKey, string.Empty);
		string text5 = PlayerPrefs.GetString(text, string.Empty);
		string text6 = PlayerPrefs.GetString(text3, string.Empty);
		if (!IsFavouriteValueForWorld(text4, worldUID, allowLegacy: false))
		{
			if (!string.IsNullOrEmpty(text4))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Ignored favourite mount from player ZDO because it belongs to another world or is an unscoped legacy value.");
			}
			text4 = string.Empty;
		}
		if (!IsFavouriteValueForWorld(text5, worldUID, allowLegacy: true))
		{
			text5 = string.Empty;
		}
		if (!IsFavouriteValueForWorld(text6, worldUID, allowLegacy: true))
		{
			text6 = string.Empty;
		}
		string source;
		string text7 = ChooseNewestFavouriteValue(text5, text6, text4, out source);
		if (!string.IsNullOrEmpty(text7))
		{
			LoadFavourite(text7);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Favourite mount restored from " + source + ": " + DescribeFavouriteState()));
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("No saved favourite mount was found for world " + ZNet.instance.GetWorldUID() + " and character " + player.GetPlayerName() + "."));
		}
		_favouriteServerStateSynchronized = true;
		if (!((ZDOID)(ref _lastRiddenAnimal)).IsNone() && !string.Equals(text4, text7, StringComparison.Ordinal))
		{
			SaveFavourite();
		}
	}

	private static string ChooseNewestFavouriteValue(string localValue, string fallbackValue, string serverValue, out string source)
	{
		string[] array = new string[3] { localValue, fallbackValue, serverValue };
		string[] array2 = new string[3] { "local player ID storage", "character-name fallback storage", "player ZDO" };
		string text = string.Empty;
		source = "none";
		long num = long.MinValue;
		for (int i = 0; i < array.Length; i++)
		{
			if (!string.IsNullOrEmpty(array[i]))
			{
				long favouriteTimestamp = GetFavouriteTimestamp(array[i]);
				if (string.IsNullOrEmpty(text) || favouriteTimestamp > num)
				{
					text = array[i];
					source = array2[i];
					num = favouriteTimestamp;
				}
			}
		}
		return text;
	}

	private static long GetFavouriteTimestamp(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return 0L;
		}
		string[] array = value.Split(new char[1] { '|' });
		if (array.Length < 9 || !long.TryParse(array[8], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
		{
			return 0L;
		}
		return result;
	}

	private static bool IsFavouriteValueForWorld(string value, long worldUid, bool allowLegacy)
	{
		if (string.IsNullOrEmpty(value))
		{
			return true;
		}
		string[] array = value.Split(new char[1] { '|' });
		if (array.Length < 10)
		{
			return allowLegacy;
		}
		if (long.TryParse(array[9], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
		{
			return result == worldUid;
		}
		return false;
	}

	private void LoadFavourite(string value)
	{
		//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_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			string[] array = value.Split(new char[1] { '|' });
			if (array.Length >= 5 && long.TryParse(array[0], out var result) && uint.TryParse(array[1], out var result2) && long.TryParse(array[2], out var result3) && uint.TryParse(array[3], out var result4))
			{
				_lastRiddenAnimal = new ZDOID(result, result2);
				_lastRiddenBoar = new ZDOID(result3, result4);
				_favouriteAnimalName = Encoding.UTF8.GetString(Convert.FromBase64String(array[4]));
				_favouriteAnimalIsBoar = ((ZDOID)(ref _lastRiddenAnimal)).Equals(_lastRiddenBoar);
				if (array.Length >= 8 && float.TryParse(array[5], NumberStyles.Float, CultureInfo.InvariantCulture, out var result5) && float.TryParse(array[6], NumberStyles.Float, CultureInfo.InvariantCulture, out var result6) && float.TryParse(array[7], NumberStyles.Float, CultureInfo.InvariantCulture, out var result7))
				{
					_favouriteLastKnownPosition = new Vector3(result5, result6, result7);
					_hasFavouriteLastKnownPosition = true;
				}
				_favouriteSavedAtUtcTicks = GetFavouriteTimestamp(value);
				if (array.Length >= 10 && long.TryParse(array[9], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result8))
				{
					_favouriteWorldUid = result8;
				}
				_favouriteAnimalDead = array.Length >= 11 && array[10] == "1";
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not restore the favourite mount: " + ex.Message));
		}
	}

	private void RememberFavourite(Character character, Player player)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: 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_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		ZDOID zDOID = character.GetZDOID();
		if (((ZDOID)(ref zDOID)).IsNone())
		{
			return;
		}
		bool flag = IsBoar(character);
		string hoverName = character.GetHoverName();
		bool flag2 = !((ZDOID)(ref _lastRiddenAnimal)).Equals(zDOID) || _favouriteAnimalName != hoverName || _favouriteAnimalIsBoar != flag;
		_lastRiddenAnimal = zDOID;
		_favouriteAnimalName = hoverName;
		_favouriteAnimalIsBoar = flag;
		_favouriteAnimalDead = false;
		bool flag3 = !_hasFavouriteLastKnownPosition || (Time.unscaledTime >= _nextFavouritePositionSave && Vector3.Distance(_favouriteLastKnownPosition, ((Component)character).transform.position) >= 10f);
		if (flag3)
		{
			_favouriteLastKnownPosition = ((Component)character).transform.position;
			_hasFavouriteLastKnownPosition = true;
			_nextFavouritePositionSave = Time.unscaledTime + 5f;
		}
		if (flag)
		{
			_lastRiddenBoar = zDOID;
			ZNetView component = ((Component)character).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner() && component.GetZDO().GetLong(FavouriteOwnerKey, 0L) == 0L)
			{
				component.GetZDO().Set(FavouriteOwnerKey, player.GetPlayerID());
			}
		}
		if (flag2)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Favourite mount changed to " + FormatZdoId(zDOID) + " (name: " + hoverName + ", boar: " + flag + ")."));
		}
		if (flag2 || flag3)
		{
			SaveFavourite(flag2);
		}
	}

	private void SaveFavourite(bool logDetails = false)
	{
		if (!string.IsNullOrEmpty(_favouritePreferenceKey) && !((ZDOID)(ref _lastRiddenAnimal)).IsNone())
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				_favouriteWorldUid = ZNet.instance.GetWorldUID();
			}
			_favouriteSavedAtUtcTicks = DateTime.UtcNow.Ticks;
			string text = Convert.ToBase64String(Encoding.UTF8.GetBytes(_favouriteAnimalName ?? string.Empty));
			string text2 = ((ZDOID)(ref _lastRiddenAnimal)).UserID + "|" + ((ZDOID)(ref _lastRiddenAnimal)).ID + "|" + ((ZDOID)(ref _lastRiddenBoar)).UserID + "|" + ((ZDOID)(ref _lastRiddenBoar)).ID + "|" + text;
			text2 = ((!_hasFavouriteLastKnownPosition) ? (text2 + "|0|0|0") : (text2 + "|" + _favouriteLastKnownPosition.x.ToString("R", CultureInfo.InvariantCulture) + "|" + _favouriteLastKnownPosition.y.ToString("R", CultureInfo.InvariantCulture) + "|" + _favouriteLastKnownPosition.z.ToString("R", CultureInfo.InvariantCulture)));
			text2 = text2 + "|" + _favouriteSavedAtUtcTicks.ToString(CultureInfo.InvariantCulture);
			text2 = text2 + "|" + _favouriteWorldUid.ToString(CultureInfo.InvariantCulture);
			text2 = text2 + "|" + (_favouriteAnimalDead ? "1" : "0");
			PlayerPrefs.SetString(_favouritePreferenceKey, text2);
			if (!string.IsNullOrEmpty(_favouriteFallbackPreferenceKey))
			{
				PlayerPrefs.SetString(_favouriteFallbackPreferenceKey, text2);
			}
			PlayerPrefs.Save();
			Player localPlayer = Player.m_localPlayer;
			ZNetView val = (Object.op_Implicit((Object)(object)localPlayer) ? ((Component)localPlayer).GetComponent<ZNetView>() : null);
			if (Object.op_Implicit((Object)(object)val) && val.IsValid() && val.IsOwner())
			{
				val.GetZDO().Set(FavouriteMountStateKey, text2);
			}
			if (logDetails)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Favourite mount saved to PlayerPrefs and player ZDO: " + DescribeFavouriteState()));
			}
		}
	}

	private string DescribeFavouriteState()
	{
		//IL_0032: 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)
		string text = (_hasFavouriteLastKnownPosition ? ((Vector3)(ref _favouriteLastKnownPosition)).ToString("F1") : "unknown");
		return "animal=" + FormatZdoId(_lastRiddenAnimal) + ", boar=" + FormatZdoId(_lastRiddenBoar) + ", name=" + _favouriteAnimalName + ", position=" + text + ", savedUtcTicks=" + _favouriteSavedAtUtcTicks + ", world=" + _favouriteWorldUid + ", dead=" + _favouriteAnimalDead;
	}

	internal void PreserveFavouriteForPortal(Character boar, Player player)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)boar) && Object.op_Implicit((Object)(object)player))
		{
			RememberFavourite(boar, player);
			_favouriteLastKnownPosition = ((Component)boar).transform.position;
			_hasFavouriteLastKnownPosition = true;
			SaveFavourite(logDetails: true);
		}
	}

	internal void MarkFavouriteDead(Character boar)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: 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_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)boar) && !((ZDOID)(ref _lastRiddenAnimal)).IsNone())
		{
			ZDOID zDOID = boar.GetZDOID();
			if (!((ZDOID)(ref zDOID)).IsNone() && ((ZDOID)(ref zDOID)).Equals(_lastRiddenAnimal) && !_favouriteAnimalDead)
			{
				_favouriteLastKnownPosition = ((Component)boar).transform.position;
				_hasFavouriteLastKnownPosition = true;
				_favouriteAnimalName = boar.GetHoverName();
				_favouriteAnimalDead = true;
				RemoveFavouriteAnimalPin();
				SaveFavourite(logDetails: true);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Favourite boar died; keeping its final map position: " + DescribeFavouriteState()));
			}
		}
	}

	internal void RestoreMountedBoarAfterPortal(MountedBoarPortalState state)
	{
		if (state != null)
		{
			((MonoBehaviour)this).StartCoroutine(RestoreMountedBoarAfterPortalRoutine(state));
		}
	}

	private IEnumerator RestoreMountedBoarAfterPortalRoutine(MountedBoarPortalState state)
	{
		yield return null;
		float deadline = Time.unscaledTime + 5f;
		while (Time.unscaledTime < deadline && Object.op_Implicit((Object)(object)state.Player) && Object.op_Implicit((Object)(object)state.Boar))
		{
			Sadle saddle = (Object.op_Implicit((Object)(object)state.Saddle) ? state.Saddle : ((Component)state.Boar).GetComponentInChildren<Sadle>(true));
			ZNetView val = ((!Object.op_Implicit((Object)(object)saddle)) ? ((ZNetView)null) : ((ZNetView)AccessTools.Field(typeof(Sadle), "m_nview").GetValue(saddle)));
			if (Object.op_Implicit((Object)(object)saddle) && Object.op_Implicit((Object)(object)val) && val.IsValid())
			{
				if (val.IsOwner())
				{
					val.GetZDO().Set(ZDOVars.s_user, 0L);
				}
				object[] array = new object[1];
				ZDOID zDOID = ((Character)state.Player).GetZDOID();
				array[0] = ((ZDOID)(ref zDOID)).UserID;
				val.InvokeRPC("RequestControl", array);
				yield return (object)new WaitForSecondsRealtime(0.15f);
				IDoodadController doodadController = state.Player.GetDoodadController();
				if ((Object)(object)((doodadController is Sadle) ? doodadController : null) == (Object)(object)saddle && ((Character)state.Player).IsAttached())
				{
					PreserveFavouriteForPortal(state.Boar, state.Player);
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Mounted boar control restored after portal travel.");
					yield break;
				}
			}
			yield return (object)new WaitForSecondsRealtime(0.2f);
		}
		((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not restore mounted boar control after portal travel.");
	}

	private static string FormatZdoId(ZDOID id)
	{
		if (!((ZDOID)(ref id)).IsNone())
		{
			return ((ZDOID)(ref id)).UserID + ":" + ((ZDOID)(ref id)).ID;
		}
		return "None";
	}

	private void ForgetFavourite()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		RemoveFavouriteAnimalPin();
		_lastRiddenAnimal = ZDOID.None;
		_lastRiddenBoar = ZDOID.None;
		_favouriteAnimalName = string.Empty;
		_favouriteAnimalIsBoar = false;
		_favouriteAnimalDead = false;
		_favouriteLastKnownPosition = Vector3.zero;
		_hasFavouriteLastKnownPosition = false;
		if (!string.IsNullOrEmpty(_favouritePreferenceKey))
		{
			PlayerPrefs.DeleteKey(_favouritePreferenceKey);
			if (!string.IsNullOrEmpty(_favouriteFallbackPreferenceKey))
			{
				PlayerPrefs.DeleteKey(_favouriteFallbackPreferenceKey);
			}
			PlayerPrefs.Save();
		}
		Player localPlayer = Player.m_localPlayer;
		ZNetView val = (Object.op_Implicit((Object)(object)localPlayer) ? ((Component)localPlayer).GetComponent<ZNetView>() : null);
		if (Object.op_Implicit((Object)(object)val) && val.IsValid() && val.IsOwner())
		{
			val.GetZDO().Set(FavouriteMountStateKey, string.Empty);
		}
	}

	private void EnsureKickRpcRegistered()
	{
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null && _registeredKickRpc != instance)
		{
			instance.Register<ZDOID, long>("RideableBoars_KickRider", (Action<long, ZDOID, long>)OnKickRider);
			_registeredKickRpc = instance;
		}
	}

	private void OnKickRider(long sender, ZDOID boarId, long ownerPlayerId)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if (!Object.op_Implicit((Object)(object)localPlayer))
		{
			return;
		}
		IDoodadController doodadController = localPlayer.GetDoodadController();
		Sadle val = (Sadle)(object)((doodadController is Sadle) ? doodadController : null);
		Character val2 = (Object.op_Implicit((Object)(object)val) ? val.GetCharacter() : null);
		if (!Object.op_Implicit((Object)(object)val2) || !IsBoar(val2))
		{
			return;
		}
		ZDOID zDOID = val2.GetZDOID();
		if (!((ZDOID)(ref zDOID)).Equals(boarId))
		{
			return;
		}
		ZNetView component = ((Component)val2).GetComponent<ZNetView>();
		if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.GetZDO().GetLong(FavouriteOwnerKey, 0L) == ownerPlayerId)
		{
			localPlayer.StopDoodadControl();
			if (((Character)localPlayer).IsAttached())
			{
				((Character)localPlayer).AttachStop();
			}
			((Character)localPlayer).Message((MessageType)2, "$boarriding_you_were_ejected", 0, (Sprite)null, false);
		}
	}

	private void UpdateFavouriteAnimalPin()
	{
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: 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_01c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f3: 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_0349: Unknown result type (might be due to invalid IL or missing references)
		//IL_034a: 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_02a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
		if (Time.unscaledTime < _nextFavouritePinUpdate)
		{
			return;
		}
		_nextFavouritePinUpdate = Time.unscaledTime + 1f;
		Minimap instance = Minimap.instance;
		if (!Object.op_Implicit((Object)(object)instance) || ((ZDOID)(ref _lastRiddenAnimal)).IsNone())
		{
			RemoveFavouriteAnimalPin();
			return;
		}
		if (_favouritePinMap != instance)
		{
			if (_favouriteAnimalPin != null && Object.op_Implicit((Object)(object)_favouritePinMap))
			{
				_favouritePinMap.RemovePin(_favouriteAnimalPin);
			}
			_favouriteAnimalPin = null;
			_favouritePinAnimal = ZDOID.None;
			_favouritePinMap = instance;
		}
		GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(_lastRiddenAnimal) : null);
		Vector3 val2;
		if (Object.op_Implicit((Object)(object)val))
		{
			Tameable component = val.GetComponent<Tameable>();
			Character component2 = val.GetComponent<Character>();
			bool flag = Object.op_Implicit((Object)(object)component2) && IsBoar(component2) && Object.op_Implicit((Object)(object)component) && component.IsTamed();
			if (flag && component2.IsDead())
			{
				MarkFavouriteDead(component2);
				val2 = _favouriteLastKnownPosition;
			}
			else if (!flag)
			{
				if (!_hasFavouriteLastKnownPosition)
				{
					RemoveFavouriteAnimalPin();
					return;
				}
				val2 = _favouriteLastKnownPosition;
				if (!_favouritePinMismatchReported)
				{
					_favouritePinMismatchReported = true;
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Favourite ZDO " + FormatZdoId(_lastRiddenAnimal) + " resolved to an incomplete or different object. Keeping the saved map position until the boar loads."));
				}
			}
			else
			{
				_favouritePinMismatchReported = false;
				val2 = (_favouriteLastKnownPosition = val.transform.position);
				_hasFavouriteLastKnownPosition = true;
				_favouriteAnimalName = component2.GetHoverName();
				_favouriteAnimalIsBoar = true;
			}
		}
		else
		{
			ZDO val3 = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(_lastRiddenAnimal) : null);
			if (val3 == null)
			{
				if (!_hasFavouriteLastKnownPosition)
				{
					RemoveFavouriteAnimalPin();
					return;
				}
				val2 = _favouriteLastKnownPosition;
			}
			else
			{
				val2 = (_favouriteLastKnownPosition = val3.GetPosition());
				_hasFavouriteLastKnownPosition = true;
			}
		}
		if (_favouriteAnimalPin == null || !((ZDOID)(ref _favouritePinAnimal)).Equals(_lastRiddenAnimal))
		{
			RemoveFavouriteAnimalPin();
			string text = (_favouriteAnimalDead ? "$boarriding_favourite_boar_dead" : (_favouriteAnimalIsBoar ? "$boarriding_favourite_boar" : "$boarriding_favourite_mount"));
			string text2 = ((Localization.instance != null) ? Localization.instance.Localize(text) : (_favouriteAnimalDead ? "Favourite Boar died" : (_favouriteAnimalIsBoar ? "Favourite Boar" : "Favourite Mount")));
			string text3 = ((Localization.instance != null) ? Localization.instance.Localize(_favouriteAnimalName) : _favouriteAnimalName);
			string text4 = (string.IsNullOrEmpty(text3) ? text2 : (text2 + ": " + text3));
			_favouriteAnimalPin = instance.AddPin(val2, (PinType)10, text4, false, false, 0L, default(PlatformUserID));
			_favouritePinMap = instance;
			_favouritePinAnimal = _lastRiddenAnimal;
			if (_favouriteAnimalIsBoar)
			{
				if ((Object)(object)_favouriteBoarIcon == (Object)null)
				{
					_favouriteBoarIcon = LoadModSprite("boar_favourite_map.png");
				}
				if ((Object)(object)_favouriteBoarIcon != (Object)null)
				{
					_favouriteAnimalPin.m_icon = _favouriteBoarIcon;
				}
			}
			_favouriteAnimalPin.m_doubleSize = false;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Favourite map pin created: " + DescribeFavouriteState()));
		}
		_favouriteAnimalPin.m_pos = val2;
	}

	private void UpdateFavouriteHealthRegeneration(Player player)
	{
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		if (Time.time < _nextFavouriteHeal)
		{
			return;
		}
		_nextFavouriteHeal = Time.time + 1f;
		if (!Object.op_Implicit((Object)(object)player) || _favouriteAnimalDead || ((ZDOID)(ref _lastRiddenAnimal)).IsNone() || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return;
		}
		GameObject val = ZNetScene.instance.FindInstance(_lastRiddenAnimal);
		Character val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Character>() : null);
		Tameable val3 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Tameable>() : null);
		ZNetView val4 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<ZNetView>() : null);
		if (Object.op_Implicit((Object)(object)val2) && !val2.IsDead() && Object.op_Implicit((Object)(object)val3) && val3.IsTamed() && Object.op_Implicit((Object)(object)val4) && val4.IsValid() && val4.GetZDO().GetLong(FavouriteOwnerKey, 0L) == player.GetPlayerID())
		{
			MonsterAI component = val.GetComponent<MonsterAI>();
			if (FavouriteRegenerationInCombat.Value || (!val2.InAttack() && (!Object.op_Implicit((Object)(object)component) || !((Object)(object)((BaseAI)component).GetTargetCreature() != (Object)null))))
			{
				val2.Heal(FavouriteHealthRegeneration.Value, false);
			}
		}
	}

	private void UpdateRidingProgression(Character riddenBoar)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: 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_0044: 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_0086: 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_004d: 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_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		if (!BoarLevelSystemEnabled.Value || !IsBoar(riddenBoar))
		{
			_progressionRiddenBoar = ZDOID.None;
			_progressionDistance = 0f;
			_progressionRideTime = 0f;
			return;
		}
		ZDOID zDOID = riddenBoar.GetZDOID();
		if (!((ZDOID)(ref _progressionRiddenBoar)).Equals(zDOID))
		{
			_progressionRiddenBoar = zDOID;
			_progressionLastPosition = ((Component)riddenBoar).transform.position;
			_progressionDistance = 0f;
			_progressionRideTime = 0f;
		}
		float num = Vector3.Distance(_progressionLastPosition, ((Component)riddenBoar).transform.position);
		_progressionLastPosition = ((Component)riddenBoar).transform.position;
		if (num > 0.02f && num < 10f)
		{
			_progressionDistance += num;
			_progressionRideTime += Time.deltaTime;
		}
		if (_progressionDistance >= 25f)
		{
			int num2 = Mathf.FloorToInt(_progressionDistance / 25f);
			_progressionDistance -= (float)num2 * 25f;
			AwardBoarExperience(riddenBoar, (float)num2 * BoarRidingDistanceExperience.Value);
		}
		if (_progressionRideTime >= 60f)
		{
			int num3 = Mathf.FloorToInt(_progressionRideTime / 60f);
			_progressionRideTime -= (float)num3 * 60f;
			AwardBoarExperience(riddenBoar, (float)num3 * BoarRidingTimeExperience.Value);
		}
	}

	internal static int GetBoarProgressionLevel(Character boar)
	{
		ZNetView val = (Object.op_Implicit((Object)(object)boar) ? ((Component)boar).GetComponent<ZNetView>() : null);
		if (!Object.op_Implicit((Object)(object)val) || !val.IsValid())
		{
			return 1;
		}
		return Mathf.Max(1, val.GetZDO().GetInt(BoarLevelKey, 1));
	}

	internal static float GetExperienceRequiredForLevel(int level)
	{
		if (!((Object)(object)Instance == (Object)null))
		{
			return Instance.BoarBaseExperience.Value * Mathf.Pow(Instance.BoarExperienceGrowth.Value, (float)Mathf.Max(0, level - 1));
		}
		return 100f;
	}

	private static int RollWildBoarLevel()
	{
		if ((Object)(object)Instance == (Object)null)
		{
			return 1;
		}
		int i = 1;
		int num = Mathf.Max(1, Instance.BoarMaximumLevel.Value);
		float num2 = Mathf.Clamp(Instance.WildBoarNextLevelChance.Value, 0.01f, 0.95f);
		for (; i < num; i++)
		{
			if (!(Random.value < num2))
			{
				break;
			}
		}
		return i;
	}

	private static float GetBoarHealthBonus(Character boar, int level)
	{
		if ((Object)(object)Instance == (Object)null || level <= 1)
		{
			return 0f;
		}
		int num = ((!Object.op_Implicit((Object)(object)boar)) ? 1 : Mathf.Max(1, boar.GetLevel()));
		float num2 = ((num >= 3) ? Instance.TwoStarBoarHealthPerLevel.Value : ((num == 2) ? Instance.OneStarBoarHealthPerLevel.Value : Instance.BoarHealthPerLevel.Value));
		float num3 = 0f;
		for (int i = 2; i <= level; i++)
		{
			int num4 = (i - 1) / 5;
			num3 += num2 + (float)num4 * Instance.BoarHealthIncreaseEveryFiveLevels.Value;
		}
		return num3;
	}

	private static string GetBoarNameWithoutLevel(Character boar)
	{
		string text = (Object.op_Implicit((Object)(object)boar) ? boar.GetHoverName() : "Boar");
		int num = text.LastIndexOf(" – Level ", StringComparison.Ordinal);
		if (num < 0)
		{
			num = text.LastIndexOf(" - Level ", StringComparison.Ordinal);
		}
		if (num <= 0)
		{
			return text;
		}
		return text.Substring(0, num);
	}

	internal void SetFavouriteForTesting(Character boar)
	{
		Player localPlayer = Player.m_localPlayer;
		if (Object.op_Implicit((Object)(object)localPlayer) && IsBoar(boar))
		{
			RememberFavourite(boar, localPlayer);
		}
	}

	internal static void EnsureBoarProgression(Character boar)
	{
		if (!Object.op_Implicit((Object)(object)boar) || (Object)(object)Instance == (Object)null || !Instance.BoarLevelSystemEnabled.Value || !IsBoar(boar))
		{
			return;
		}
		Tameable component = ((Component)boar).GetComponent<Tameable>();
		ZNetView component2 = ((Component)boar).GetComponent<ZNetView>();
		if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component2) || !component2.IsValid())
		{
			return;
		}
		ZDO zDO = component2.GetZDO();
		int num = zDO.GetInt(BoarLevelKey, 0);
		if (num <= 0 && component2.IsOwner())
		{
			num = (component.IsTamed() ? 1 : RollWildBoarLevel());
			zDO.Set(BoarLevelKey, num, false);
			zDO.Set(BoarExperienceKey, 0f);
		}
		int level = Mathf.Clamp((num <= 0) ? 1 : num, 1, Instance.BoarMaximumLevel.Value);
		float num2 = zDO.GetFloat(BoarBaseHealthKey, 0f);
		if (num2 <= 0f)
		{
			num2 = boar.GetMaxHealth();
			if (component2.IsOwner())
			{
				zDO.Set(BoarBaseHealthKey, num2);
			}
		}
		float num3 = num2 + GetBoarHealthBonus(boar, level);
		MethodInfo methodInfo = AccessTools.Method(typeof(Character), "SetMaxHealth", new Type[1] { typeof(float) }, (Type[])null);
		if (methodInfo != null && Mathf.Abs(boar.GetMaxHealth() - num3) > 0.01f)
		{
			methodInfo.Invoke(boar, new object[1] { num3 });
		}
	}

	internal static void AwardBoarExperience(Character boar, float amount)
	{
		//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0308: Unknown result type (might be due to invalid IL or missing references)
		if (amount <= 0f || !Object.op_Implicit((Object)(object)boar) || (Object)(object)Instance == (Object)null || !Instance.BoarLevelSystemEnabled.Value || !IsBoar(boar))
		{
			return;
		}
		Tameable component = ((Component)boar).GetComponent<Tameable>();
		ZNetView component2 = ((Component)boar).GetComponent<ZNetView>();
		if (!Object.op_Implicit((Object)(object)component) || !component.IsTamed() || !Object.op_Implicit((Object)(object)component2) || !component2.IsValid())
		{
			return;
		}
		if (!component2.IsOwner())
		{
			component2.ClaimOwnership();
		}
		if (!component2.IsOwner())
		{
			return;
		}
		EnsureBoarProgression(boar);
		ZDO zDO = component2.GetZDO();
		int num = Mathf.Clamp(zDO.GetInt(BoarLevelKey, 1), 1, Instance.BoarMaximumLevel.Value);
		if (num >= Instance.BoarMaximumLevel.Value)
		{
			return;
		}
		float num2 = Instance.BoarExperienceRate.Value;
		Player localPlayer = Player.m_localPlayer;
		if (Object.op_Implicit((Object)(object)localPlayer))
		{
			float num3 = ((Character)localPlayer).GetSkillFactor((SkillType)110) * 100f;
			float num4 = 1f + num3 * 0.5f;
			float num5 = Mathf.Clamp01(Mathf.Max(0f, num4 - (float)num) / 15f) * Instance.BoarCatchUpMultiplier.Value;
			num2 += num5;
		}
		float num6 = zDO.GetFloat(BoarExperienceKey, 0f) + amount * num2;
		bool flag = false;
		float maxHealth = boar.GetMaxHealth();
		while (num < Instance.BoarMaximumLevel.Value)
		{
			float num7 = Instance.BoarBaseExperience.Value * Mathf.Pow(Instance.BoarExperienceGrowth.Value, (float)(num - 1));
			if (num6 < num7)
			{
				break;
			}
			num6 -= num7;
			num++;
			flag = true;
		}
		zDO.Set(BoarLevelKey, num, false);
		zDO.Set(BoarExperienceKey, num6);
		EnsureBoarProgression(boar);
		if (flag)
		{
			boar.Heal(Mathf.Max(0f, boar.GetMaxHealth() - maxHealth), false);
		}
		if (flag && Instance.ShowLevelUpEffect.Value && Object.op_Implicit((Object)(object)Player.m_localPlayer))
		{
			string format = ((Localization.instance != null) ? Localization.instance.Localize("$boarriding_level_up_message") : "{0} - reached Level {1}");
			((Character)Player.m_localPlayer).Message((MessageType)2, string.Format(format, GetBoarNameWithoutLevel(boar), num), 0, (Sprite)null, false);
			FieldInfo fieldInfo = AccessTools.Field(typeof(Tameable), "m_sootheEffect");
			object obj = ((fieldInfo != null) ? fieldInfo.GetValue(component) : null);
			MethodInfo methodInfo = ((obj != null) ? AccessTools.Method(obj.GetType(), "Create", new Type[5]
			{
				typeof(Vector3),
				typeof(Quaternion),
				typeof(Transform),
				typeof(float),
				typeof(int)
			}, (Type[])null) : null);
			if (methodInfo != null)
			{
				methodInfo.Invoke(obj, new object[5]
				{
					((Component)boar).transform.position,
					Quaternion.identity,
					((Component)boar).transform,
					1f,
					-1
				});
			}
		}
	}

	private void AwardCommandExperience(Character boar)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: 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)
		if (Object.op_Implicit((Object)(object)boar) && !(BoarCommandExperience.Value <= 0f))
		{
			ZDOID zDOID = boar.GetZDOID();
			if (!_nextCommandExperience.TryGetValue(zDOID, out var value) || !(Time.time < value))
			{
				_nextCommandExperience[zDOID] = Time.time + 30f;
				AwardBoarExperience(boar, BoarCommandExperience.Value);
			}
		}
	}

	private void RemoveFavouriteAnimalPin()
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		if (_favouriteAnimalPin != null && Object.op_Implicit((Object)(object)_favouritePinMap))
		{
			_favouritePinMap.RemovePin(_favouriteAnimalPin);
		}
		_favouriteAnimalPin = null;
		_favouritePinAnimal = ZDOID.None;
		_favouritePinMap = null;
	}

	internal static Sprite LoadModSprite(string filename)
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Expected O, but got Unknown
		//IL_00d4: 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)
		if ((Object)(object)Instance == (Object)null)
		{
			return null;
		}
		string path = ResolveModAssetPath(Path.Combine("icons", filename));
		if (!File.Exists(path))
		{
			return null;
		}
		try
		{
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			MethodInfo methodInfo = AccessTools.Method(typeof(ImageConversion), "LoadImage", new Type[3]
			{
				typeof(Texture2D),
				typeof(byte[]),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo == null || !(bool)methodInfo.Invoke(null, new object[3]
			{
				val,
				File.ReadAllBytes(path),
				false
			}))
			{
				return null;
			}
			((Object)val).name = Path.GetFileNameWithoutExtension(filename);
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
		}
		catch (Exception ex)
		{
			LogError("Icon could not be loaded: " + ex.Message);
			return null;
		}
	}

	private static string ResolveModAssetPath(string relativePath)
	{
		string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location);
		string text = Path.Combine(directoryName, relativePath);
		if (File.Exists(text))
		{
			return text;
		}
		return Path.Combine(directoryName, "RideableBoars", relativePath);
	}

	private static void CloseCommandWheel(RadialBase radial)
	{
		if (Object.op_Implicit((Object)(object)radial))
		{
			AccessTools.Method(typeof(RadialBase), "Close", (Type[])null, (Type[])null).Invoke(radial, new object[1] { true });
		}
	}

	internal unsafe static string GetLocalizedShortcut(KeyboardShortcut shortcut)
	{
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Invalid comparison between Unknown and I4
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Invalid comparison between Unknown and I4
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: 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_00a3: Invalid comparison between Unknown and I4
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Invalid comparison between Unknown and I4
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Invalid comparison between Unknown and I4
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Invalid comparison between Unknown and I4
		bool flag = Localization.instance != null && (string.Equals(Localization.instance.GetSelectedLanguage(), "German", StringComparison.OrdinalIgnoreCase) || string.Equals(Localization.instance.GetSelectedLanguage(), "Deutsch", StringComparison.OrdinalIgnoreCase));
		List<string> list = new List<string>();
		foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
		{
			if ((int)modifier == 304)
			{
				list.Add(flag ? "Umschalt" : "Left Shift");
			}
			else if ((int)modifier == 303)
			{
				list.Add(flag ? "Umschalt rechts" : "Right Shift");
			}
			else if ((int)modifier == 306)
			{
				list.Add(flag ? "Strg" : "Left Control");
			}
			else if ((int)modifier == 305)
			{
				list.Add(flag ? "Strg rechts" : "Right Control");
			}
			else if ((int)modifier == 308)
			{
				list.Add(flag ? "Alt" : "Left Alt");
			}
			else if ((int)modifier == 307)
			{
				list.Add(flag ? "Alt Gr" : "Right Alt");
			}
			else
			{
				list.Add(((object)(*(KeyCode*)(&modifier))/*cast due to .constrained prefix*/).ToString());
			}
		}
		list.Add(((object)((KeyboardShortcut)(ref shortcut)).MainKey/*cast due to .constrained prefix*/).ToString());
		return string.Join(" + ", list.ToArray());
	}

	internal static bool IsBoar(Character character)
	{
		if (Object.op_Implicit((Object)(object)character))
		{
			return ((Object)((Component)character).gameObject).name.StartsWith("Boar", StringComparison.OrdinalIgnoreCase);
		}
		return false;
	}

	internal static bool HasSaddle(Tameable tameable)
	{
		if (!Object.op_Implicit((Object)(object)tameable) || !Object.op_Implicit((Object)(object)tameable.m_saddle))
		{
			return false;
		}
		MethodInfo methodInfo = AccessTools.Method(typeof(Tameable), "HaveSaddle", (Type[])null, (Type[])null);
		if (methodInfo != null)
		{
			return (bool)methodInfo.Invoke(tameable, null);
		}
		return false;
	}

	internal bool CommandLastBoar(BoarCommand command)
	{
		//IL_00a3: 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_0186: Unknown result type (might be due to invalid IL or missing references)
		//IL_0191: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		switch (command)
		{
		case BoarCommand.EjectRider:
			return EjectFavouriteRider(localPlayer);
		case BoarCommand.TargetFavourite:
			ResetLoadedBoarCommands(localPlayer);
			_commandAllTamedBoars = false;
			return ShowCommandMessage("$boarriding_scope_favourite");
		case BoarCommand.TargetAll:
			ResetLoadedBoarCommands(localPlayer);
			_commandAllTamedBoars = true;
			return ShowCommandMessage("$boarriding_scope_all");
		default:
		{
			if (_favouriteAnimalDead)
			{
				return ShowCommandMessage("$boarriding_favourite_dead_command");
			}
			if (_commandAllTamedBoars)
			{
				return CommandAllLoadedBoars(command, localPlayer);
			}
			if (!Object.op_Implicit((Object)(object)localPlayer) || ((ZDOID)(ref _lastRiddenBoar)).IsNone())
			{
				return ShowCommandMessage("$boarriding_no_boar");
			}
			GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(_lastRiddenBoar) : null);
			Tameable val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Tameable>() : null);
			Character val3 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Character>() : null);
			if ((!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val3) || !IsBoar(val3) || !Object.op_Implicit((Object)(object)val2) || !val2.IsTamed()) && TryResolveFavouriteBoarZdo(localPlayer) != null && Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				val = ZNetScene.instance.FindInstance(_lastRiddenBoar);
			}
			if (!Object.op_Implicit((Object)(object)val) && command == BoarCommand.Recall)
			{
				return RecallUnloadedFavouriteBoar(localPlayer);
			}
			Tameable val4 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Tameable>() : null);
			MonsterAI val5 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<MonsterAI>() : null);
			if (!Object.op_Implicit((Object)(object)val4) || !val4.IsTamed() || !Object.op_Implicit((Object)(object)val5))
			{
				return ShowCommandMessage("$boarriding_no_boar");
			}
			float num = Vector3.Distance(((Component)localPlayer).transform.position, val.transform.position);
			if (command == BoarCommand.Follow && num > FollowRange.Value)
			{
				return ShowCommandMessage("$boarriding_follow_too_far");
			}
			if (command == BoarCommand.Recall && num > RecallRange.Value)
			{
				return ShowCommandMessage("$boarriding_out_of_range");
			}
			if (command != BoarCommand.Wait)
			{
				StopOtherBoarsFollowingPlayer(localPlayer, val);
			}
			ApplyCommandState(val4, val5, localPlayer, command);
			AwardCommandExperience(val.GetComponent<Character>());
			return ShowCommandMessage(command switch
			{
				BoarCommand.Roam => "$boarriding_roaming", 
				BoarCommand.Recall => "$boarriding_recalled", 
				BoarCommand.Wait => "$boarriding_waiting", 
				_ => "$boarriding_following", 
			});
		}
		}
	}

	private bool RecallUnloadedFavouriteBoar(Player player)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: 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)
		//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_00e3: 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_0174: 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_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: 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_01b4: 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_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0205: Unknown result type (might be due to invalid IL or missing references)
		//IL_020c: 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_024b: Unknown result type (might be due to invalid IL or missing references)
		//IL_022e: Unknown result type (might be due to invalid IL or missing references)
		//IL_022f: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)player) || ZDOMan.instance == null)
		{
			return ShowCommandMessage("$boarriding_no_boar");
		}
		Vector3 val = (_hasFavouriteLastKnownPosition ? _favouriteLastKnownPosition : ((Component)player).transform.position);
		float num = Vector3.Distance(((Component)player).transform.position, val);
		if (num > RecallRange.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Unloaded favourite recall rejected: animal=" + FormatZdoId(_lastRiddenBoar) + ", distance=" + num.ToString("F1", CultureInfo.InvariantCulture) + ", range=" + RecallRange.Value.ToString("F1", CultureInfo.InvariantCulture) + "."));
			return ShowCommandMessage("$boarriding_out_of_range");
		}
		ZDO val2 = ZDOMan.instance.GetZDO(_lastRiddenBoar);
		if (val2 == null || val2.GetPrefab() != StringExtensionMethods.GetStableHashCode("Boar"))
		{
			val2 = TryResolveFavouriteBoarZdo(player);
		}
		if (val2 == null)
		{
			ZDOMan.instance.RequestZDO(_lastRiddenBoar);
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Favourite recall could not resolve boar " + FormatZdoId(_lastRiddenBoar) + " in world " + _favouriteWorldUid + ". A ZDO request was sent."));
			return ShowCommandMessage("$boarriding_no_boar");
		}
		val = val2.GetPosition();
		Vector3 val3 = ((Component)player).transform.position - ((Component)player).transform.forward * 3f + ((Component)player).transform.right * 1.5f;
		float num2 = default(float);
		if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(val3, ref num2))
		{
			val3.y = num2 + 0.2f;
		}
		else
		{
			val3.y = ((Component)player).transform.position.y + 0.2f;
		}
		val2.SetPosition(val3);
		_favouriteLastKnownPosition = val3;
		_hasFavouriteLastKnownPosition = true;
		SaveFavourite();
		if (_favouriteAnimalPin != null)
		{
			_favouriteAnimalPin.m_pos = val3;
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Recalled unloaded favourite boar " + FormatZdoId(_lastRiddenBoar) + " from " + ((Vector3)(ref val)).ToString("F1") + " to " + ((Vector3)(ref val3)).ToString("F1") + "."));
		return ShowCommandMessage("$boarriding_recalled");
	}

	private ZDO TryResolveFavouriteBoarZdo(Player player)
	{
		//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01db: 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_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0225: Unknown result type (might be due to invalid IL or missing references)
		//IL_0238: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: 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_015c: Unknown result type (might be due to invalid IL or missing references)
		if (ZDOMan.instance == null || !_hasFavouriteLastKnownPosition)
		{
			return null;
		}
		List<ZDO> list = new List<ZDO>();
		int num = 0;
		while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("Boar", list, ref num))
		{
		}
		string text = StripBoarLevelSuffix(_favouriteAnimalName);
		ZDO val = null;
		float num2 = float.MaxValue;
		foreach (ZDO item in list)
		{
			if (item == null || !item.IsValid() || !item.GetBool(ZDOVars.s_tamed, false))
			{
				continue;
			}
			if (((ZDOID)(ref item.m_uid)).Equals(_lastRiddenBoar) && (Object)(object)ZNetScene.instance != (Object)null)
			{
				GameObject val2 = ZNetScene.instance.FindInstance(item.m_uid);
				Character character = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<Character>() : null);
				Tameable val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<Tameable>() : null);
				if (Object.op_Implicit((Object)(object)val2) && (!IsBoar(character) || !Object.op_Implicit((Object)(object)val3) || !val3.IsTamed()))
				{
					continue;
				}
			}
			string a = item.GetString(ZDOVars.s_tamedName, string.Empty);
			bool flag = !string.IsNullOrEmpty(text) && string.Equals(a, text, StringComparison.OrdinalIgnoreCase);
			bool flag2 = Object.op_Implicit((Object)(object)player) && item.GetLong(FavouriteOwnerKey, 0L) == player.GetPlayerID();
			float num3 = Vector3.Distance(_favouriteLastKnownPosition, item.GetPosition());
			if (flag || flag2 || (string.IsNullOrEmpty(text) && !(num3 > 8f)))
			{
				float num4 = num3 + (flag ? 0f : 1000f) + (flag2 ? 0f : 100f);
				if (!(num4 >= num2))
				{
					val = item;
					num2 = num4;
				}
			}
		}
		if (val == null)
		{
			return null;
		}
		ZDOID lastRiddenBoar = _lastRiddenBoar;
		_lastRiddenBoar = val.m_uid;
		_lastRiddenAnimal = val.m_uid;
		_favouriteLastKnownPosition = val.GetPosition();
		_hasFavouriteLastKnownPosition = true;
		SaveFavourite(logDetails: true);
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Resolved favourite boar ZDO from " + FormatZdoId(lastRiddenBoar) + " to " + FormatZdoId(val.m_uid) + " by persistent world data."));
		return val;
	}

	private static string StripBoarLevelSuffix(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return string.Empty;
		}
		int num = name.LastIndexOf(" – Level ", StringComparison.Ordinal);
		if (num < 0)
		{
			num = name.LastIndexOf(" - Level ", StringComparison.Ordinal);
		}
		if (num <= 0)
		{
			return name;
		}
		return name.Substring(0, num);
	}

	private bool EjectFavouriteRider(Player player)
	{
		//IL_0033: 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_0113: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)player) || ((ZDOID)(ref _lastRiddenBoar)).IsNone() || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return ShowCommandMessage("$boarriding_no_boar");
		}
		GameObject val = ZNetScene.instance.FindInstance(_lastRiddenBoar);
		Character val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Character>() : null);
		ZNetView val3 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<ZNetView>() : null);
		if (!Object.op_Implicit((Object)(object)val2) || !IsBoar(val2) || !Object.op_Implicit((Object)(object)val3) || !val3.IsValid())
		{
			return ShowCommandMessage("$boarriding_no_boar");
		}
		if (val3.GetZDO().GetLong(FavouriteOwnerKey, 0L) != player.GetPlayerID())
		{
			return ShowCommandMessage("$boarriding_not_owner");
		}
		long num = val3.GetZDO().GetLong(ZDOVars.s_user, 0L);
		if (num != 0L)
		{
			ZDOID zDOID = ((Character)player).GetZDOID();
			if (num != ((ZDOID)(ref zDOID)).UserID)
			{
				EnsureKickRpcRegistered();
				if (_registeredKickRpc == null)
				{
					return ShowCommandMessage("$boarriding_no_other_rider");
				}
				_registeredKickRpc.InvokeRoutedRPC(num, "RideableBoars_KickRider", new object[2]
				{
					_lastRiddenBoar,
					player.GetPlayerID()
				});
				return ShowCommandMessage("$boarriding_eject_sent");
			}
		}
		return ShowCommandMessage("$boarriding_no_other_rider");
	}

	private static void StopOtherBoarsFollowingPlayer(Player player, GameObject favourite)
	{
		foreach (Character allCharacter in Character.GetAllCharacters())
		{
			if (!IsBoar(allCharacter) || (Object)(object)((Component)allCharacter).gameObject == (Object)(object)favourite)
			{
				continue;
			}
			Tameable component = ((Component)allCharacter).GetComponent<Tameable>();
			MonsterAI component2 = ((Component)allCharacter).GetComponent<MonsterAI>();
			if (Object.op_Implicit((Object)(object)component) && component.IsTamed() && Object.op_Implicit((Object)(object)component2) && (Object)(object)component2.GetFollowTarget() == (Object)(object)((Component)player).gameObject)
			{
				BoarWaitState boarWaitState = ((Component)allCharacter).GetComponent<BoarWaitState>();
				if (!Object.op_Implicit((Object)(object)boarWaitState))
				{
					boarWaitState = ((Component)allCharacter).gameObject.AddComponent<BoarWaitState>();
				}
				boarWaitState.Waiting = true;
				boarWaitState.FreeRoam = false;
				component.Command((Humanoid)(object)player, false);
			}
		}
	}

	private static void ResetLoadedBoarCommands(Player player)
	{
		if (!Object.op_Implicit((Object)(object)player))
		{
			return;
		}
		foreach (Character allCharacter in Character.GetAllCharacters())
		{
			if (!IsBoar(allCharacter))
			{
				continue;
			}
			Tameable component = ((Component)allCharacter).GetComponent<Tameable>();
			MonsterAI component2 = ((Component)allCharacter).GetComponent<MonsterAI>();
			if (Object.op_Implicit((Object)(object)component) && component.IsTamed() && Object.op_Implicit((Object)(object)component2) && (Object)(object)component2.GetFollowTarget() == (Object)(object)((Component)player).gameObject)
			{
				BoarWaitState boarWaitState = ((Component)allCharacter).GetComponent<BoarWaitState>();
				if (!Object.op_Implicit((Object)(object)boarWaitState))
				{
					boarWaitState = ((Component)allCharacter).gameObject.AddComponent<BoarWaitState>();
				}
				boarWaitState.Waiting = true;
				component.Command((Humanoid)(object)player, false);
			}
		}
	}

	private bool CommandAllLoadedBoars(BoarCommand command, Player player)
	{
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)player))
		{
			return false;
		}
		int num = 0;
		foreach (Character allCharacter in Character.GetAllCharacters())
		{
			if (!IsBoar(allCharacter))
			{
				continue;
			}
			Tameable component = ((Component)allCharacter).GetComponent<Tameable>();
			MonsterAI component2 = ((Component)allCharacter).GetComponent<MonsterAI>();
			if (Object.op_Implicit((Object)(object)component) && component.IsTamed() && Object.op_Implicit((Object)(object)component2))
			{
				float num2 = Vector3.Distance(((Component)player).transform.position, ((Component)allCharacter).transform.position);
				if ((command != BoarCommand.Follow || !(num2 > FollowRange.Value)) && (command != BoarCommand.Recall || !(num2 > RecallRange.Value)))
				{
					ApplyCommandState(component, component2, player, command);
					AwardCommandExperience(allCharacter);
					num++;
				}
			}
		}
		if (num == 0)
		{
			return ShowCommandMessage("$boarriding_no_loaded_boars");
		}
		return ShowCommandMessage(command switch
		{
			BoarCommand.Roam => "$boarriding_roaming", 
			BoarCommand.Wait => "$boarriding_waiting", 
			_ => "$boarriding_following", 
		});
	}

	private static void ApplyCommandState(Tameable tameable, MonsterAI ai, Player player, BoarCommand command)
	{
		BoarWaitState boarWaitState = ((Component)tameable).GetComponent<BoarWaitState>();
		if (!Object.op_Implicit((Object)(object)boarWaitState))
		{
			boarWaitState = ((Component)tameable).gameObject.AddComponent<BoarWaitState>();
		}
		if (command == BoarCommand.Roam)
		{
			boarWaitState.Waiting = false;
			boarWaitState.FreeRoam = true;
			if ((Object)(object)ai.GetFollowTarget() == (Object)(object)((Component)player).gameObject)
			{
				tameable.Command((Humanoid)(object)player, false);
			}
			((BaseAI)ai).ResetPatrolPoint();
			return;
		}
		boarWaitState.Waiting = command == BoarCommand.Wait;
		boarWaitState.FreeRoam = false;
		GameObject followTarget = ai.GetFollowTarget();
		bool flag = command != BoarCommand.Wait;
		bool flag2 = (Object)(object)followTarget == (Object)(object)((Component)player).gameObject;
		if ((flag && !flag2) || (!flag && (Object)(object)followTarget != (Object)null))
		{
			tameable.Command((Humanoid)(object)player, false);
		}
	}

	private static bool ShowCommandMessage(string text)
	{
		if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
		{
			((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
		}
		return true;
	}

	private void OnD