Decompiled source of ValheimFortress v0.30.4

plugins/ValheimFortress.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using ValheimFortress.Challenge;
using ValheimFortress.Defenses;
using ValheimFortress.common;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ValheimFortress")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ValheimFortress")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.1.0")]
namespace ValheimFortress
{
	internal class ValheimFortressPieces
	{
		public ValheimFortressPieces()
		{
			if (VFConfig.EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Loading Pieces.");
			}
			LoadAlterOfChallenge();
			LoadDefenses();
		}

		private void LoadAlterOfChallenge()
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			dictionary.Add("name", "Alter of Challenge");
			dictionary.Add("catagory", "Misc");
			dictionary.Add("prefab", "VFshrine_of_challenge");
			dictionary.Add("sprite", "shrine_of_challenge");
			dictionary.Add("requiredBench", "piece_workbench");
			new JotunnPiece(dictionary, new Dictionary<string, bool>(), new Dictionary<string, Tuple<int, bool>>
			{
				{
					"Stone",
					Tuple.Create(23, item2: true)
				},
				{
					"Ruby",
					Tuple.Create(4, item2: true)
				},
				{
					"Coins",
					Tuple.Create(100, item2: false)
				}
			});
			Dictionary<string, string> dictionary2 = new Dictionary<string, string>();
			dictionary2.Add("name", "Alter of the Arena");
			dictionary2.Add("catagory", "Misc");
			dictionary2.Add("prefab", "VFshine_of_gladiator");
			dictionary2.Add("sprite", "alter_of_arena");
			dictionary2.Add("requiredBench", "piece_workbench");
			new JotunnPiece(dictionary2, new Dictionary<string, bool>(), new Dictionary<string, Tuple<int, bool>>
			{
				{
					"Stone",
					Tuple.Create(23, item2: true)
				},
				{
					"Coins",
					Tuple.Create(100, item2: false)
				}
			});
		}

		private void LoadDefenses()
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			dictionary.Add("name", "Stone Spikes");
			dictionary.Add("catagory", "Misc");
			dictionary.Add("prefab", "VFstone_stakes");
			dictionary.Add("sprite", "stone_spikes");
			dictionary.Add("requiredBench", "piece_stonecutter");
			new JotunnPiece(dictionary, new Dictionary<string, bool>(), new Dictionary<string, Tuple<int, bool>>
			{
				{
					"Stone",
					Tuple.Create(30, item2: false)
				},
				{
					"Silver",
					Tuple.Create(2, item2: true)
				}
			});
			Dictionary<string, string> dictionary2 = new Dictionary<string, string>();
			dictionary2.Add("name", "Auto Ballista");
			dictionary2.Add("catagory", "Misc");
			dictionary2.Add("prefab", "VFpiece_turret");
			dictionary2.Add("sprite", "modified_turret");
			dictionary2.Add("requiredBench", "piece_artisanstation");
			new JotunnPiece(dictionary2, new Dictionary<string, bool>(), new Dictionary<string, Tuple<int, bool>>
			{
				{
					"BlackMetal",
					Tuple.Create(20, item2: false)
				},
				{
					"YggdrasilWood",
					Tuple.Create(20, item2: false)
				},
				{
					"MechanicalSpring",
					Tuple.Create(5, item2: true)
				},
				{
					"DragonTear",
					Tuple.Create(2, item2: true)
				}
			});
		}
	}
	[BepInPlugin("MidnightsFX.ValheimFortress", "ValheimFortress", "0.30.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal class ValheimFortress : BaseUnityPlugin
	{
		public const string PluginGUID = "MidnightsFX.ValheimFortress";

		public const string PluginName = "ValheimFortress";

		public const string PluginVersion = "0.30.4";

		public static AssetBundle EmbeddedResourceBundle;

		public VFConfig cfg;

		public static GameObject spawnPortal;

		public static GameObject creatureNotifier;

		public static GameObject portalDestroyVFX;

		private static readonly Regex sWhitespace = new Regex("\\s+");

		private void Awake()
		{
			cfg = new VFConfig(((BaseUnityPlugin)this).Config);
			cfg.SetupConfigRPCs();
			EmbeddedResourceBundle = AssetUtils.LoadAssetBundleFromResources("ValheimFortress.AssetsEmbedded.vfbundle", typeof(ValheimFortress).Assembly);
			AddLocalizations();
			new ValheimFortressPieces();
			SetupVFXObjects(EmbeddedResourceBundle);
			new VFLocations(EmbeddedResourceBundle, cfg);
			VFConfig.GetYamlConfigFiles();
			Levels.UpdateLevelValues(cfg);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Valheim Fortress loaded.");
		}

		private void AddLocalizations()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Loading Localizations.");
			string[] manifestResourceNames = typeof(ValheimFortress).Assembly.GetManifestResourceNames();
			foreach (string text in manifestResourceNames)
			{
				if (text.Contains("Localizations"))
				{
					string input = ReadEmbeddedResourceFile(text);
					string text2 = Regex.Replace(input, "\\/\\/.*", "");
					string[] array = text.Split(new char[1] { '.' });
					if (VFConfig.EnableDebugMode.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Adding localization: " + array[2]));
					}
					localization.AddJsonFile(array[2], text2);
				}
			}
		}

		internal static GameObject getPortal()
		{
			return spawnPortal;
		}

		internal static GameObject getNotifier()
		{
			return creatureNotifier;
		}

		internal static GameObject getPortalDestroyVFX()
		{
			return portalDestroyVFX;
		}

		private static void SetupVFXObjects(AssetBundle EmbeddedResourceBundle)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			GameObject val = EmbeddedResourceBundle.LoadAsset<GameObject>("Assets/Custom/Pieces/VFortress/VF_portal.prefab");
			CustomPrefab val2 = new CustomPrefab(val, false);
			PrefabManager.Instance.AddPrefab(val2);
			spawnPortal = val2.Prefab;
			GameObject val3 = EmbeddedResourceBundle.LoadAsset<GameObject>("Assets/Custom/Pieces/VFortress/VF_portal_destroy.prefab");
			CustomPrefab val4 = new CustomPrefab(val3, false);
			PrefabManager.Instance.AddPrefab(val4);
			portalDestroyVFX = val4.Prefab;
			GameObject val5 = EmbeddedResourceBundle.LoadAsset<GameObject>("Assets/Custom/Pieces/VFortress/VF_creature_notify.prefab");
			CustomPrefab val6 = new CustomPrefab(val5, false);
			PrefabManager.Instance.AddPrefab(val6);
			creatureNotifier = val6.Prefab;
		}

		public static string LocalizeOrDefault(string str_to_localize, string default_string)
		{
			string text = Localization.instance.Localize(str_to_localize);
			if (text == "[" + str_to_localize.Replace("$", "") + "]")
			{
				Logger.LogDebug((object)(str_to_localize + " was not localized, returning the default: " + default_string));
				return default_string;
			}
			return text;
		}

		internal static string ReadEmbeddedResourceFile(string filename)
		{
			using Stream stream = typeof(ValheimFortress).Assembly.GetManifestResourceStream(filename);
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}

		public static List<string> shuffleList(List<string> inputList)
		{
			int i = 0;
			int count = inputList.Count;
			int num = 0;
			string text = null;
			List<string> list = new List<string>();
			list.AddRange(inputList);
			for (; i < count; i++)
			{
				num = Random.Range(i, list.Count);
				text = list[i];
				list[i] = list[num];
				list[num] = text;
			}
			return list;
		}

		public static string FormatJson(string json, string indent = "  ")
		{
			int indentation = 0;
			int quoteCount = 0;
			int escapeCount = 0;
			IEnumerable<string> enumerable = from ch in json ?? string.Empty
				let escaped = ((ch == '\\') ? escapeCount++ : ((escapeCount > 0) ? escapeCount-- : escapeCount)) > 0
				let quotes = (ch == '"' && !escaped) ? quoteCount++ : quoteCount
				let unquoted = quotes % 2 == 0
				let colon = (ch == ':' && unquoted) ? ": " : null
				let nospace = (char.IsWhiteSpace(ch) && unquoted) ? string.Empty : null
				let lineBreak = (ch == ',' && unquoted) ? string.Concat(ch.ToString(), Environment.NewLine, string.Concat(Enumerable.Repeat(indent, indentation))) : null
				let openChar = ((ch == '{' || ch == '[') && unquoted) ? string.Concat(ch.ToString(), Environment.NewLine, string.Concat(Enumerable.Repeat(indent, ++indentation))) : ch.ToString()
				let closeChar = ((ch == '}' || ch == ']') && unquoted) ? string.Concat(Environment.NewLine, string.Concat(Enumerable.Repeat(indent, --indentation)), ch.ToString()) : ch.ToString()
				select colon ?? nospace ?? lineBreak ?? ((openChar.Length > 1) ? openChar : closeChar);
			return string.Concat(enumerable);
		}

		public static string ReplaceWhitespace(string input, string replacement)
		{
			return sWhitespace.Replace(input, replacement);
		}
	}
	internal class VFConfig
	{
		public static ConfigFile cfg;

		public static ConfigEntry<bool> EnableDebugMode;

		public static ConfigEntry<bool> EnableTurretDebugMode;

		public static ConfigEntry<bool> BallistaTargetsPassives;

		public static ConfigEntry<float> BallistaDamage;

		public static ConfigEntry<float> BallistaRange;

		public static ConfigEntry<float> BallistaAmmoAccuracyPenalty;

		public static ConfigEntry<short> BallistaTargetUpdateCacheInterval;

		public static ConfigEntry<bool> BallistaEnableShotSafetyCheck;

		public static ConfigEntry<float> BallistaCooldownTime;

		public static ConfigEntry<short> MaxSpawnRange;

		public static ConfigEntry<float> rewardsMultiplier;

		public static ConfigEntry<float> rewardsDifficultyScalar;

		public static ConfigEntry<bool> EnableBossModifier;

		public static ConfigEntry<bool> EnableHardModifier;

		public static ConfigEntry<bool> EnableSiegeModifer;

		public static ConfigEntry<bool> EnableRewardsEstimate;

		public static ConfigEntry<bool> EnableMapPings;

		public static ConfigEntry<short> MaxRewardsPerSecond;

		public static ConfigEntry<short> NotifyCreatureThreshold;

		public static ConfigEntry<short> TeleportCreatureThreshold;

		public static ConfigEntry<short> ShrineReconnectPauseBetweenAmount;

		public static ConfigEntry<float> ShrineAnnouncementRange;

		public static ConfigEntry<float> DistanceBetweenShrines;

		public static ConfigEntry<float> ShrineReconnectRange;

		public static ConfigEntry<short> NumberOfEachWildShrine;

		public static ConfigEntry<short> ChallengeShrineMaxCreaturesPerWave;

		public static ConfigEntry<short> ArenaShrineMaxCreaturesPerWave;

		public static ConfigEntry<float> ShrineRewardPlayerBonus;

		public static ConfigEntry<bool> ServerConfigsLocked;

		private static CustomRPC monsterSyncRPC;

		private static CustomRPC rewardSyncRPC;

		private static CustomRPC WavesSyncRPC;

		private static CustomRPC LevelsSyncRPC;

		private static CustomRPC WildShrineSyncRPC;

		private static string rewardFilePath = Path.Combine(Paths.ConfigPath, "VFortress", "Rewards.yaml");

		private static string creatureFilePath = Path.Combine(Paths.ConfigPath, "VFortress", "SpawnableCreatures.yaml");

		private static string waveStylesFilePath = Path.Combine(Paths.ConfigPath, "VFortress", "WaveStyles.yaml");

		private static string levelDefinitionsFilePath = Path.Combine(Paths.ConfigPath, "VFortress", "Levels.yaml");

		private static string wildShrineConfigurationFilePath = Path.Combine(Paths.ConfigPath, "VFortress", "WildShrines.yaml");

		public VFConfig(ConfigFile Config)
		{
			cfg = Config;
			cfg.SaveOnConfigSet = true;
			CreateConfigValues(Config);
			string configPath = Paths.ConfigPath;
			FileSystemWatcher fileSystemWatcher = new FileSystemWatcher();
			fileSystemWatcher.Path = configPath;
			fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
			fileSystemWatcher.Filter = "MidnightsFX.ValheimFortress.cfg";
			fileSystemWatcher.Changed += UpdateMainConfigFile;
			fileSystemWatcher.Created += UpdateMainConfigFile;
			fileSystemWatcher.Renamed += UpdateMainConfigFile;
			fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher.EnableRaisingEvents = true;
			Logger.LogInfo((object)"Main config filewatcher initialized.");
		}

		public void SetupConfigRPCs()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0028: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_0054: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_0080: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00ac: Expected O, but got Unknown
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			//IL_00d8: Expected O, but got Unknown
			monsterSyncRPC = NetworkManager.Instance.AddRPC("monsteryaml_rpc", new CoroutineHandler(OnServerRecieveConfigs), new CoroutineHandler(OnClientReceiveCreatureConfigs));
			rewardSyncRPC = NetworkManager.Instance.AddRPC("rewardsyaml_rpc", new CoroutineHandler(OnServerRecieveConfigs), new CoroutineHandler(OnClientReceiveRewardsConfigs));
			WavesSyncRPC = NetworkManager.Instance.AddRPC("wavestyleyaml_rpc", new CoroutineHandler(OnServerRecieveConfigs), new CoroutineHandler(OnClientReceiveWaveConfigs));
			LevelsSyncRPC = NetworkManager.Instance.AddRPC("levelsyaml_rpc", new CoroutineHandler(OnServerRecieveConfigs), new CoroutineHandler(OnClientReceiveLevelConfigs));
			WildShrineSyncRPC = NetworkManager.Instance.AddRPC("wildshrineyaml_rpc", new CoroutineHandler(OnServerRecieveConfigs), new CoroutineHandler(OnClientReceiveWildShrineConfigs));
			SynchronizationManager.Instance.AddInitialSynchronization(monsterSyncRPC, (Func<ZPackage>)SendCreatureConfigs);
			SynchronizationManager.Instance.AddInitialSynchronization(rewardSyncRPC, (Func<ZPackage>)SendRewardsConfigs);
			SynchronizationManager.Instance.AddInitialSynchronization(WavesSyncRPC, (Func<ZPackage>)SendWavesConfigs);
			SynchronizationManager.Instance.AddInitialSynchronization(LevelsSyncRPC, (Func<ZPackage>)SendLevelsConfigs);
			SynchronizationManager.Instance.AddInitialSynchronization(WildShrineSyncRPC, (Func<ZPackage>)SendWildShrineConfigs);
		}

		public static string GetSecondaryConfigDirectoryPath()
		{
			string path = Path.Combine(Paths.ConfigPath, "VFortress");
			DirectoryInfo directoryInfo = Directory.CreateDirectory(path);
			return directoryInfo.FullName;
		}

		public static void GetYamlConfigFiles()
		{
			string secondaryConfigDirectoryPath = GetSecondaryConfigDirectoryPath();
			bool flag = false;
			bool flag2 = false;
			bool flag3 = false;
			bool flag4 = false;
			bool flag5 = false;
			string[] files = Directory.GetFiles(secondaryConfigDirectoryPath);
			string[] array = files;
			foreach (string text in array)
			{
				if (EnableDebugMode.Value)
				{
					Logger.LogInfo((object)("Config file found: " + text));
				}
				if (text.Contains("Rewards.yaml"))
				{
					if (EnableDebugMode.Value)
					{
						Logger.LogInfo((object)("Found rewards configuration: " + text));
					}
					rewardFilePath = text;
					flag = true;
				}
				if (text.Contains("SpawnableCreatures.yaml"))
				{
					if (EnableDebugMode.Value)
					{
						Logger.LogInfo((object)("Found Creature configuration: " + text));
					}
					creatureFilePath = text;
					flag2 = true;
				}
				if (text.Contains("WaveStyles.yaml"))
				{
					if (EnableDebugMode.Value)
					{
						Logger.LogInfo((object)("Found WaveStyles configuration: " + text));
					}
					waveStylesFilePath = text;
					flag3 = true;
				}
				if (text.Contains("Levels.yaml"))
				{
					if (EnableDebugMode.Value)
					{
						Logger.LogInfo((object)("Found Levels configuration: " + text));
					}
					levelDefinitionsFilePath = text;
					flag4 = true;
				}
				if (text.Contains("WildShrines.yaml"))
				{
					if (EnableDebugMode.Value)
					{
						Logger.LogInfo((object)("Found WildShrine configuration: " + text));
					}
					wildShrineConfigurationFilePath = text;
					flag5 = true;
				}
			}
			if (!flag)
			{
				Logger.LogInfo((object)"Rewards file missing, recreating.");
				using StreamWriter streamWriter = new StreamWriter(rewardFilePath);
				string value = "#################################################\n# Shrine of Challenge Rewards Configuration\n#################################################\n# Rewards configurations have a number of key values\n#  Coin:                                 |- The name of the reward, this will be the diplayed name if there is no localization for this reward, which is likely the case for any custom entries.\n#    enabled: true                       |- Whether or not the reward is enabled, you can use this to disable any vanilla rewards you do not want. At least 1 reward must be available at ALL times.\n#    resource_cost: 5                    |- This is the cost to gain 1 of the particular reward. Points are generated based on how many monsters are spawned.\n#    resource_prefab: \"Coins\"            |- This is the unity prefab name for a resource, you will often see mods list the prefabs they have added. Prefabs are also listed on the valheim wiki.\n#    required_boss: \"None\"               |- This must be one of the following values: \"None\" \"Eikythr\" \"TheElder\" \"BoneMass\" \"Moder\" \"Yagluth\" \"TheQueen\"\n";
				streamWriter.WriteLine(value);
				streamWriter.WriteLine(RewardsData.YamlRewardsDefinition());
			}
			if (!flag3)
			{
				Logger.LogInfo((object)"WaveStyles file missing, recreating.");
				using StreamWriter streamWriter2 = new StreamWriter(waveStylesFilePath);
				string value2 = "#################################################\n# Shrine of Challenge WaveStyles Configuration\n#################################################\n# WaveStyles configurations have a number of key values\n# Easy:                      |- This is the key used to lookup this wave definition\n#  WaveConfig                |- The wave configuration for each segment of the wave\n#   - type: COMMON           |- This is the catagory of creature that will be selected\n#     percent: 30            |- This is the percentage of the waves total point pool that will be used for this spawn";
				streamWriter2.WriteLine(value2);
				streamWriter2.WriteLine(Levels.YamlWaveDefinition());
			}
			if (!flag4)
			{
				Logger.LogInfo((object)"LevelsConfig file missing, recreating.");
				using StreamWriter streamWriter3 = new StreamWriter(levelDefinitionsFilePath);
				string value3 = "#################################################\n# Shrines of Challenge Levels Configuration\n#################################################\n# levels:\n# - levelIndex: 1                                                  |- LevelIndex is the difficulty this wave is set at, valid values are 1+\n#   numPhases: 4                                                   |- The number of phases in this level, enemies will be distributed among the phases\n#   levelForShrineTypes:                                           |- What shrines will host this level, multiple definitions can be applied\n#     challenge: true                                              |-   Shrine of challenge will host this level\n#     arena: true                                                  |-   Shrine of the arena will host this level\n#   levelMenuLocalization: $shrine_menu_meadow                     |- This is the localization that will be displayed when selecting the level, if no key matches the $lookup the literal string will be used\n#   requiredGlobalKey: NONE                                        |- This is the global key required to unlock this level more available here (https://valheim.fandom.com/wiki/Global_Keys)\n#   biome: Meadows                                                 |- This is the biome used for this level. This determines what creatures are considered\n#   waveFormat: Tutorial                                           |- This is the format of the wave, formates are defined in WaveStyles.yaml, it determines how many creatures, what catagory and percentage of total points they use\n#   bossWaveFormat: TutorialBoss                                   |- This is the format if the wave is modified to be a boss wave\n#   maxCreatureFromPreviousBiomes: 0                               |- This is the maximum number of creatures that can be selected from prior biomes\n#   previousBiomeSearchRange: 1                                    |- How many previous biomes to look back for potential creatures\n#   chancePreviousBiomeCreatureSelected: 0.05                      |- Chance that a creature will be selected from a previous biome, if it can be\n#   previousBiomeCreaturesAddedStarPerBiome: true                  |- Automatically upgrades creatures from a previous biome, one star per biome difference (up to max defined stars)\n#   levelWarningLocalization: $shrine_warning_meadows              |- This is the announcement text that plays when the challenge starts as a normal wave, uses literal value if the localization does not exist\n#   bossLevelWarningLocalization: $shrine_warning_meadows_boss     |- This is the announcement text that plays when the challenge starts as a boss wave, localizations are available here https://github.com/MidnightsFX/Valheim_Fortress/blob/master/JotunnModStub/Localizations/English.json\n#   onlySelectMonsters: []                                         |- This is an array of monsters that are the only valid targets for this wave\n#   excludeSelectMonsters: []                                      |- This is an array of monsters that are to be avoided for the wave\n#   levelRewardOptionsLimitedTo:                                   |- When set, only the available rewards can be selected for this level, rewards still have their normal global key requirements\r\n#     - Coin                                                       |- The rewards entry name of rewards that should be available for this level\n#   commonSpawnModifiers:                                          |- Spawn modifiers are functions applied to each part of the wave, they can be different per catagory of monster\n#     linearIncreaseRandomWaveAdjustment: true                     |-   In general, it is best to only use one type of spawn modifier per creature type\n#     linearDecreaseRandomWaveAdjustment: false                    |- Linear Decrease/Increase will frontload or backload this creature in the various phase of the wave, meaning more of it will appear earlier or later depending on the modifier\n#     partialRandomWaveAdjustment: false                           |- Partial random adjustment will add more significant random variance to the number of creatures that will spawn\n#     onlyGenerateInSecondHalf: false                              |- Only generate in second half will prevent this type of creature from spawning in the earlier waves, this is useful for Elites/Rares when LinearDecrease is set for commons\n#   rareSpawnModifiers:                                            |-   The start of the wave will have many commons, and they will taper off till the end, while elites would come into play only on the second half of the wave\n#     linearIncreaseRandomWaveAdjustment: true\n#     linearDecreaseRandomWaveAdjustment: false\n#     partialRandomWaveAdjustment: false\n#     onlyGenerateInSecondHalf: false\n#   eliteSpawnModifiers:\n#     linearIncreaseRandomWaveAdjustment: true\n#     linearDecreaseRandomWaveAdjustment: false\n#     partialRandomWaveAdjustment: false\n#     onlyGenerateInSecondHalf: false\n#   uniqueSpawnModifiers: ";
				streamWriter3.WriteLine(value3);
				streamWriter3.WriteLine(Levels.YamlLevelsDefinition());
			}
			if (!flag2)
			{
				Logger.LogInfo((object)"CreatureConfig file missing, recreating.");
				using StreamWriter streamWriter4 = new StreamWriter(creatureFilePath);
				string value4 = "#################################################\n# Shrine of Challenge Creature Configuration\n#################################################\n# Creature configurations have a number of key values\n# Neck:                    |- This is the name of the creature being added, it is primarily used for display purposes and lookups\n#  spawnCost: 5            |- This is how many points from the wave pool it costs to spawn one creature, smaller values allow many more spawns.\n#  prefab: \"Neck\"          |- This is the creatures prefab, which will be used to spawn it.\n#  spawnType: \"common\"     |- This can either be: \"common\" or \"rare\" or \"elite\" or \"unique\", uniques are \"bosses\", most of the wave will be made up of more common enemies\n#  enabled: true           |- This controls if this creature will be included in wave-generation.\n#  dropsEnabled: false     |- This controls if this particular monster should drop loot. Disabled by default for everything.\n#  biome: \"Meadows\"        |- This must be one of the following values: \"Meadows\", \"BlackForest\", \"Swamp\", \"Mountain\", \"Plains\", \"Mistlands\". The biome determines the levels that will recieve this spawn, and how the spawn might be adjusted to\n#                             fit higher difficulty waves. eg: a greydwarf spawning into a swamp level wave will recieve 1 bonus star, since it is from the black forest, which is 1 biome behind the swamp.";
				streamWriter4.WriteLine(value4);
				streamWriter4.WriteLine(Levels.YamlCreatureDefinition());
			}
			if (!flag5)
			{
				Logger.LogInfo((object)"WildShrineConfig file missing, recreating.");
				using StreamWriter streamWriter5 = new StreamWriter(wildShrineConfigurationFilePath);
				string value5 = "###################################################################################################################################################\n# Wild Shrine Configuration\n###################################################################################################################################################\n# wildShrines:\n# - definitionForWildShrine: VF_wild_shrine_green1                    |- The prefab that this set of configuration will be applied to\n#   wildShrineNameLocalization: $wild_shrine_green                    |- The localization for the prefabs name (when hovered over) this uses a lookup value but defaults to its literal value\n#   wildShrineRequestLocalization: $wild_shrine_green_request         |- What the shrine says when you interact with it\n#   shrineUnacceptedTributeLocalization: $wild_shrine_not_interested  |- What the shrine says when you offer an incorrect tribute\n#   shrineLargerTributeRequiredLocalization: $wild_shrine_hungry      |- What the shrine says when you do not offer enough tribute\n#   wildShrineLevelsConfig:                                           |- Level configurations related to this shrine\n#   - tributeName: TrophyBoar                                         |- The prefab name of the tribute required to activate this level\n#     tributeAmount: 4                                                |- Amount of the tribute required to activate this level\n#     rewards:                                                        |- Rewards for this level in the format of Prefab: cost eg: RawMeat: 14.\n#       LeatherScraps: 14\n#       RawMeat: 12\n#     hardMode: false                                                 |- If hardmode should be enabled for this level (doubles the spawn point pool and gives 50% more rewards)\n#     siegeMode: false                                                |- If siege mode should be enabled for this level (double the number of waves 4->8 and gives 50% more rewards)\n#     wildshrineWaveStartLocalization: $wild_boars_attack             |- Localization text to display when this wave starts\n#     wildshrineWaveEndLocalization: $wild_boars_defeated             |- Localization text to display when this wave is finished\n#     wildLevelDefinition:\n#       levelIndex: 2                                                 |- The difficulty level for this wave, valid values are 1+ (Refer to the readme for a breakdown of this equation)\n#       biome: Meadows                                                |- The biome this wave is for, this impacts creature selection\n#       waveFormat: Tutorial                                          |- The wavestyle this uses (from wavestyles.yml), this governs which catagories and the percentage makeup of the wave\n#       levelWarningLocalization: $meadows_warning_wilderness         |- Localization for a between phase warning (often not used)\n#       maxCreaturesPerPhaseOverride: 15                              |- Overrides the max creatures per wave to be this value (overrides the global config)\n#       onlySelectMonsters:                                           |- Set of monsters that can be selected (From monsters.yml)\n#       - Boar\n#       - Greyling\n#       excludeSelectMonsters:                                        |- Set of monsters that can't be selected (from monsters.yml) best used when OnlySelected is not set.\n#       commonSpawnModifiers:                                         |- Spawn modifiers for common creatures\n#         linearIncreaseRandomWaveAdjustment: true\n#       rareSpawnModifiers:                                           |- Spawn modifiers for rare creatures\n#       eliteSpawnModifiers:                                          |- Spawn modifiers for elite creatures\n";
				streamWriter5.WriteLine(value5);
				streamWriter5.WriteLine(WildShrineData.YamlWildShrineDefinition());
			}
			string input = File.ReadAllText(creatureFilePath);
			string input2 = File.ReadAllText(rewardFilePath);
			string input3 = File.ReadAllText(waveStylesFilePath);
			string input4 = File.ReadAllText(levelDefinitionsFilePath);
			string input5 = File.ReadAllText(wildShrineConfigurationFilePath);
			try
			{
				WaveFormatCollection waveStyles = CONST.yamldeserializer.Deserialize<WaveFormatCollection>(input3);
				Levels.UpdateWaveDefinition(waveStyles);
			}
			catch (Exception arg)
			{
				Logger.LogWarning((object)$"There was an error updating the waveStyle values, defaults will be used. Exception: {arg}");
			}
			try
			{
				SpawnableCreatureCollection spawnables = CONST.yamldeserializer.Deserialize<SpawnableCreatureCollection>(input);
				Levels.UpdateSpawnableCreatures(spawnables);
			}
			catch (Exception arg2)
			{
				Logger.LogWarning((object)$"There was an error updating the creature values, defaults will be used. Exception: {arg2}");
			}
			try
			{
				RewardEntryCollection rewards = CONST.yamldeserializer.Deserialize<RewardEntryCollection>(input2);
				RewardsData.UpdateRewardsEntries(rewards);
			}
			catch (Exception arg3)
			{
				Logger.LogWarning((object)$"There was an error updating the rewards values, defaults will be used. Exception: {arg3}");
			}
			try
			{
				ChallengeLevelDefinitionCollection levelDefinitions = CONST.yamldeserializer.Deserialize<ChallengeLevelDefinitionCollection>(input4);
				Levels.UpdateLevelsDefinition(levelDefinitions);
			}
			catch (Exception arg4)
			{
				Logger.LogWarning((object)$"There was an error updating the levelDefinitions values, defaults will be used. Exception: {arg4}");
			}
			try
			{
				WildShrineConfigurationCollection wildShrineDefinitions = CONST.yamldeserializer.Deserialize<WildShrineConfigurationCollection>(input5);
				WildShrineData.UpdateWildShrineDefinition(wildShrineDefinitions);
			}
			catch (Exception arg5)
			{
				Logger.LogWarning((object)$"There was an error updating the WildShrine values, defaults will be used. Exception: {arg5}");
			}
			FileSystemWatcher fileSystemWatcher = new FileSystemWatcher();
			fileSystemWatcher.Path = secondaryConfigDirectoryPath;
			fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
			fileSystemWatcher.Filter = "Levels.yaml";
			fileSystemWatcher.Changed += UpdateLevelsConfigFileOnChange;
			fileSystemWatcher.Created += UpdateLevelsConfigFileOnChange;
			fileSystemWatcher.Renamed += UpdateLevelsConfigFileOnChange;
			fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher.EnableRaisingEvents = true;
			FileSystemWatcher fileSystemWatcher2 = new FileSystemWatcher();
			fileSystemWatcher2.Path = secondaryConfigDirectoryPath;
			fileSystemWatcher2.NotifyFilter = NotifyFilters.LastWrite;
			fileSystemWatcher2.Filter = "WaveStyles.yaml";
			fileSystemWatcher2.Changed += UpdateWavesConfigFileOnChange;
			fileSystemWatcher2.Created += UpdateWavesConfigFileOnChange;
			fileSystemWatcher2.Renamed += UpdateWavesConfigFileOnChange;
			fileSystemWatcher2.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher2.EnableRaisingEvents = true;
			FileSystemWatcher fileSystemWatcher3 = new FileSystemWatcher();
			fileSystemWatcher3.Path = secondaryConfigDirectoryPath;
			fileSystemWatcher3.NotifyFilter = NotifyFilters.LastWrite;
			fileSystemWatcher3.Filter = "SpawnableCreatures.yaml";
			fileSystemWatcher3.Changed += UpdateCreatureConfigFileOnChange;
			fileSystemWatcher3.Created += UpdateCreatureConfigFileOnChange;
			fileSystemWatcher3.Renamed += UpdateCreatureConfigFileOnChange;
			fileSystemWatcher3.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher3.EnableRaisingEvents = true;
			FileSystemWatcher fileSystemWatcher4 = new FileSystemWatcher();
			fileSystemWatcher4.Path = secondaryConfigDirectoryPath;
			fileSystemWatcher4.NotifyFilter = NotifyFilters.LastWrite;
			fileSystemWatcher4.Filter = "Rewards.yaml";
			fileSystemWatcher4.Changed += UpdateRewardsConfigFileOnChange;
			fileSystemWatcher4.Created += UpdateRewardsConfigFileOnChange;
			fileSystemWatcher4.Renamed += UpdateRewardsConfigFileOnChange;
			fileSystemWatcher4.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher4.EnableRaisingEvents = true;
			FileSystemWatcher fileSystemWatcher5 = new FileSystemWatcher();
			fileSystemWatcher5.Path = secondaryConfigDirectoryPath;
			fileSystemWatcher5.NotifyFilter = NotifyFilters.LastWrite;
			fileSystemWatcher5.Filter = "WildShrines.yaml";
			fileSystemWatcher5.Changed += UpdateWildShrineConfigFileOnChange;
			fileSystemWatcher5.Created += UpdateWildShrineConfigFileOnChange;
			fileSystemWatcher5.Renamed += UpdateWildShrineConfigFileOnChange;
			fileSystemWatcher5.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher5.EnableRaisingEvents = true;
		}

		public static IEnumerator OnServerRecieveConfigs(long sender, ZPackage package)
		{
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Server recieved config from client, rejecting due to being the server.");
			}
			yield return null;
		}

		private static ZPackage SendCreatureConfigs()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			string text = File.ReadAllText(creatureFilePath);
			ZPackage val = new ZPackage();
			val.Write(text);
			return val;
		}

		private static ZPackage SendWavesConfigs()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			string text = File.ReadAllText(waveStylesFilePath);
			ZPackage val = new ZPackage();
			val.Write(text);
			return val;
		}

		private static ZPackage SendRewardsConfigs()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			string text = File.ReadAllText(rewardFilePath);
			ZPackage val = new ZPackage();
			val.Write(text);
			return val;
		}

		private static ZPackage SendLevelsConfigs()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			string text = File.ReadAllText(levelDefinitionsFilePath);
			ZPackage val = new ZPackage();
			val.Write(text);
			return val;
		}

		private static ZPackage SendWildShrineConfigs()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			string text = File.ReadAllText(wildShrineConfigurationFilePath);
			ZPackage val = new ZPackage();
			val.Write(text);
			return val;
		}

		private static IEnumerator OnClientReceiveCreatureConfigs(long sender, ZPackage package)
		{
			string creatureyaml = package.ReadString();
			using (StreamWriter writetext = new StreamWriter(creatureFilePath))
			{
				writetext.WriteLine(creatureyaml);
			}
			yield return null;
		}

		private static IEnumerator OnClientReceiveRewardsConfigs(long sender, ZPackage package)
		{
			string rewardsyaml = package.ReadString();
			using (StreamWriter writetext = new StreamWriter(rewardFilePath))
			{
				writetext.WriteLine(rewardsyaml);
			}
			yield return null;
		}

		private static IEnumerator OnClientReceiveWaveConfigs(long sender, ZPackage package)
		{
			string wavesyaml = package.ReadString();
			using (StreamWriter writetext = new StreamWriter(waveStylesFilePath))
			{
				writetext.WriteLine(wavesyaml);
			}
			yield return null;
		}

		private static IEnumerator OnClientReceiveLevelConfigs(long sender, ZPackage package)
		{
			string levelsyaml = package.ReadString();
			if (UpdateLevelsInMemory(levelsyaml) && !ServerConfigsLocked.Value)
			{
				using StreamWriter writetext = new StreamWriter(levelDefinitionsFilePath);
				writetext.WriteLine(levelsyaml);
			}
			yield return null;
		}

		private static IEnumerator OnClientReceiveWildShrineConfigs(long sender, ZPackage package)
		{
			string levelsyaml = package.ReadString();
			using (StreamWriter writetext = new StreamWriter(wildShrineConfigurationFilePath))
			{
				writetext.WriteLine(levelsyaml);
			}
			yield return null;
		}

		private static void UpdateLevelsConfigFileOnChange(object sender, FileSystemEventArgs e)
		{
			if (!File.Exists(levelDefinitionsFilePath))
			{
				return;
			}
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)$"{e} Creature filewatcher called, updating creature values.");
			}
			string raw_levels_data = File.ReadAllText(levelDefinitionsFilePath);
			UpdateLevelsInMemory(raw_levels_data);
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Updated levels in-memory values.");
			}
			if (GUIManager.IsHeadless())
			{
				try
				{
					LevelsSyncRPC.SendPackage(ZNet.instance.m_peers, SendLevelsConfigs());
					if (EnableDebugMode.Value)
					{
						Logger.LogInfo((object)"Sent levels configs to clients.");
					}
					return;
				}
				catch
				{
					Logger.LogError((object)"Error while server syncing creature configs");
					return;
				}
			}
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Instance is not a server, and will not send znet creature updates.");
			}
		}

		private static bool UpdateLevelsInMemory(string raw_levels_data)
		{
			ChallengeLevelDefinitionCollection levelDefinitions;
			try
			{
				levelDefinitions = CONST.yamldeserializer.Deserialize<ChallengeLevelDefinitionCollection>(raw_levels_data);
			}
			catch
			{
				return false;
			}
			Levels.UpdateLevelsDefinition(levelDefinitions);
			return true;
		}

		private static void UpdateCreatureConfigFileOnChange(object sender, FileSystemEventArgs e)
		{
			if (!File.Exists(creatureFilePath))
			{
				return;
			}
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)$"{e} Creature filewatcher called, updating creature values.");
			}
			string input = File.ReadAllText(creatureFilePath);
			SpawnableCreatureCollection spawnables;
			try
			{
				spawnables = CONST.yamldeserializer.Deserialize<SpawnableCreatureCollection>(input);
			}
			catch
			{
				if (EnableDebugMode.Value)
				{
					Logger.LogWarning((object)"Creatures failed deserializing, skipping update.");
				}
				return;
			}
			Levels.UpdateSpawnableCreatures(spawnables);
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Updated creature in-memory values.");
			}
			if (GUIManager.IsHeadless())
			{
				try
				{
					monsterSyncRPC.SendPackage(ZNet.instance.m_peers, SendCreatureConfigs());
					if (EnableDebugMode.Value)
					{
						Logger.LogInfo((object)"Sent creature configs to clients.");
					}
					return;
				}
				catch
				{
					Logger.LogError((object)"Error while server syncing creature configs");
					return;
				}
			}
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Instance is not a server, and will not send znet creature updates.");
			}
		}

		private static void UpdateWavesConfigFileOnChange(object sender, FileSystemEventArgs e)
		{
			if (!File.Exists(waveStylesFilePath))
			{
				return;
			}
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)$"{e} Wavestyles filewatcher called, updating Wavestyles values.");
			}
			string input = File.ReadAllText(waveStylesFilePath);
			WaveFormatCollection waveStyles;
			try
			{
				waveStyles = CONST.yamldeserializer.Deserialize<WaveFormatCollection>(input);
			}
			catch
			{
				if (EnableDebugMode.Value)
				{
					Logger.LogWarning((object)"Wavestyles failed deserializing, skipping update.");
				}
				return;
			}
			Levels.UpdateWaveDefinition(waveStyles);
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Updated WaveDefinition in-memory values.");
			}
			if (GUIManager.IsHeadless())
			{
				try
				{
					WavesSyncRPC.SendPackage(ZNet.instance.m_peers, SendWavesConfigs());
					if (EnableDebugMode.Value)
					{
						Logger.LogInfo((object)"Sent WaveDefinition configs to clients.");
					}
					return;
				}
				catch
				{
					Logger.LogError((object)"Error while server syncing Wave configs");
					return;
				}
			}
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Instance is not a server, and will not send znet creature updates.");
			}
		}

		private static void UpdateRewardsConfigFileOnChange(object sender, FileSystemEventArgs e)
		{
			if (!File.Exists(rewardFilePath))
			{
				return;
			}
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Rewards filewatcher called, updating rewards values.");
			}
			string input = File.ReadAllText(rewardFilePath);
			RewardEntryCollection rewards;
			try
			{
				rewards = CONST.yamldeserializer.Deserialize<RewardEntryCollection>(input);
			}
			catch (Exception)
			{
				if (EnableDebugMode.Value)
				{
					Logger.LogWarning((object)"Rewards failed deserializing, skipping update.");
				}
				return;
			}
			RewardsData.UpdateRewardsEntries(rewards);
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Updated rewards in-memory values.");
			}
			if (GUIManager.IsHeadless())
			{
				try
				{
					rewardSyncRPC.SendPackage(ZNet.instance.m_peers, SendRewardsConfigs());
					if (EnableDebugMode.Value)
					{
						Logger.LogInfo((object)"Sent rewards configs to clients.");
					}
					return;
				}
				catch (Exception)
				{
					Logger.LogError((object)"Error while server syncing rewards configs");
					return;
				}
			}
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Instance is not a server, and will not send znet reward updates.");
			}
		}

		private static void UpdateWildShrineConfigFileOnChange(object sender, FileSystemEventArgs e)
		{
			if (!File.Exists(rewardFilePath))
			{
				return;
			}
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Rewards filewatcher called, updating Wildshrines values.");
			}
			string input = File.ReadAllText(wildShrineConfigurationFilePath);
			WildShrineConfigurationCollection wildShrineDefinitions;
			try
			{
				wildShrineDefinitions = CONST.yamldeserializer.Deserialize<WildShrineConfigurationCollection>(input);
			}
			catch (Exception)
			{
				if (EnableDebugMode.Value)
				{
					Logger.LogWarning((object)"WildShrineConfigs failed deserializing, skipping update.");
				}
				return;
			}
			WildShrineData.UpdateWildShrineDefinition(wildShrineDefinitions);
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Updated WildshrineConfigs in-memory values.");
			}
			if (GUIManager.IsHeadless())
			{
				try
				{
					WildShrineSyncRPC.SendPackage(ZNet.instance.m_peers, SendWildShrineConfigs());
					if (EnableDebugMode.Value)
					{
						Logger.LogInfo((object)"Sent Wildshrine configs to clients.");
					}
					return;
				}
				catch (Exception)
				{
					Logger.LogError((object)"Error while server syncing Wildshrine configs");
					return;
				}
			}
			if (EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Instance is not a server, and will not send znet reward updates.");
			}
		}

		private static void UpdateMainConfigFile(object sender, FileSystemEventArgs e)
		{
			if (!File.Exists(Paths.ConfigPath))
			{
				return;
			}
			try
			{
				cfg.SaveOnConfigSet = false;
				cfg.Reload();
				cfg.SaveOnConfigSet = true;
			}
			catch
			{
				Logger.LogError((object)"There was an issue reloading MidnightsFX.ValheimFortress.cfg.");
			}
		}

		public static ConfigEntry<bool> BindServerConfig(string catagory, string key, bool value, string description, bool advanced = false)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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)
			//IL_002e: Expected O, but got Unknown
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			return cfg.Bind<bool>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<string> BindServerConfig(string catagory, string key, string value, string description, bool advanced = false)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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)
			//IL_002e: Expected O, but got Unknown
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			return cfg.Bind<string>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<short> BindServerConfig(string catagory, string key, short value, string description, bool advanced = false, short valmin = 0, short valmax = 150)
		{
			//IL_001b: 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)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			return cfg.Bind<short>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<short>(valmin, valmax), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<float> BindServerConfig(string catagory, string key, float value, string description, bool advanced = false, float valmin = 0f, float valmax = 150f)
		{
			//IL_001b: 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)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			return cfg.Bind<float>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(valmin, valmax), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		private void CreateConfigValues(ConfigFile Config)
		{
			//IL_03b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c7: Expected O, but got Unknown
			//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Expected O, but got Unknown
			//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0402: Expected O, but got Unknown
			//IL_0402: Unknown result type (might be due to invalid IL or missing references)
			//IL_040c: Expected O, but got Unknown
			MaxSpawnRange = BindServerConfig("Shrine of Challenge", "MaxSpawnRange", 100, "The radius around the shrine that enemies can spawn in.", advanced: false, 10, 800);
			EnableHardModifier = BindServerConfig("Shrine of Challenge", "EnableHardModifier", value: true, "Whether or not the hard mode modifier is available (100% bigger wave size for 50% more rewards)", advanced: true);
			EnableBossModifier = BindServerConfig("Shrine of Challenge", "EnableBossModifier", value: true, "Whether or not boss mod is available as a level modifier (more rewards & spawns the biome specific boss)", advanced: true);
			EnableSiegeModifer = BindServerConfig("Shrine of Challenge", "EnableSiegeModifer", value: true, "Whether or not siege mode is available as a modifier. Siege mode gives much larger pauses between waves, and 100% larger waves for 50% more reward.", advanced: true);
			EnableMapPings = BindServerConfig("Shrine of Challenge", "EnableMapPings", value: false, "Whether or not waves spawning from the shrine of challenge should ping the map when they spawn.", advanced: true);
			EnableRewardsEstimate = BindServerConfig("Shrine of Challenge", "EnableRewardsEstimate", value: true, "Enables showing an estimate of how many rewards you will get for doing the selected level for the specified reward.", advanced: true);
			rewardsMultiplier = BindServerConfig("Shrine of Challenge", "rewardsMultiplier", 1.1f, "The base multiplier for rewards, higher values will make every wave more rewarding", advanced: true);
			rewardsDifficultyScalar = BindServerConfig("Shrine of Challenge", "rewardsDifficultyScalar", 0.02f, "Multiplier for rewards that scales with level, each level adds this to the value, making high level challenges much more rewarding.", advanced: true);
			MaxRewardsPerSecond = BindServerConfig("Shrine of Challenge", "MaxRewardsPerSecond", 120, "Sets how fast the shrine will spawn rewards. Reducing this will reduce the performance impact of spawning so many items at once.", advanced: true, 10, 400);
			NotifyCreatureThreshold = BindServerConfig("Shrine of Challenge", "NotifyCreatureThreshold", 10, "Sets the level at which interacting with the shrine will add notifier to remaining creatures.", advanced: true, 1, 50);
			TeleportCreatureThreshold = BindServerConfig("Shrine of Challenge", "TeleportCreatureThreshold", 3, "Sets the level at which interacting with the shrine teleport remaining creatures to the shrine.", advanced: true, 1, 50);
			ShrineAnnouncementRange = BindServerConfig("Shrine of Challenge", "ShrineAnnouncementRange", 150f, "Sets the range at which announcements will display for shrine of challenge related activities", advanced: true, 50f, 800f);
			ShrineReconnectRange = BindServerConfig("Shrine of Challenge", "ShrineReconnectRange", 150f, "Sets the max range for the shrine to scan creatures for reconnection when an area is unloaded/reloaded (this includes exit/loading singleplayer).", advanced: true, 500f, 5000f);
			ShrineReconnectPauseBetweenAmount = BindServerConfig("Shrine of Challenge", "ShrineReconnectPauseBetweenAmount", 30, "Sets the maximun number of creatures to process for reconnection in a singular second.", advanced: true, 1, 60);
			DistanceBetweenShrines = BindServerConfig("Wild Shrines", "DistanceBetweenShrines", 750f, "The mimum distance between shrines, setting this higher will result in fewer wild shrines, lower more.", advanced: true, 100f, 5000f);
			NumberOfEachWildShrine = BindServerConfig("Wild Shrines", "NumberOfEachWildShrine", 100, "Each wild shrine type will attempt to be placed this many times", advanced: true, 5, 200);
			ShrineRewardPlayerBonus = BindServerConfig("Shrine of Challenge", "ShrineRewardPlayerBonus", 1f, "How much rewards are multipled for each additional player", advanced: true, 0f, 3f);
			ChallengeShrineMaxCreaturesPerWave = BindServerConfig("Shrine of Challenge", "max_creatures_per_wave", 60, "The max number of creatures that a wave can generate with, creatures will attempt to upgrade and reduce counts based on this.", advanced: true, 12, 200);
			ArenaShrineMaxCreaturesPerWave = BindServerConfig("Shrine of Arena", "max_creatures_per_wave", 25, "The max number of creatures that a wave can generate with, creatures will attempt to upgrade and reduce counts based on this.", advanced: true, 12, 200);
			BallistaTargetsPassives = BindServerConfig("Auto Ballista", "EnableTargetingPassiveCreatures", value: false, "Whether or not the automated ballista will target passive creatures (like deer)", advanced: true);
			BallistaDamage = BindServerConfig("Auto Ballista", "BallistaDamage", 120f, "How much damage the automated ballista does per shot.", advanced: true, 10f, 5000f);
			BallistaRange = BindServerConfig("Auto Ballista", "BallistaRange", 30f, "How far the ballista can aquire targets and shoot", advanced: true, 10f, 100f);
			BallistaAmmoAccuracyPenalty = BindServerConfig("Auto Ballista", "BallistaAmmoAccuracyPenalty", 0.05f, "How inaccurate the ammo is for the ballista", advanced: true, 0f, 0.15f);
			BallistaCooldownTime = BindServerConfig("Auto Ballista", "BallistaCooldownTime", 2f, "How long the ballista waits before another shot", advanced: true, 0.5f, 10f);
			BallistaTargetUpdateCacheInterval = BindServerConfig("Auto Ballista", "BallistaTargetUpdateCacheInterval", 5, "How many ticks until the ballista updates its cache of nearby enemies.", advanced: true, 1, 10);
			BallistaEnableShotSafetyCheck = BindServerConfig("Auto Ballista", "BallistaEnableShotSafetyCheck", value: true, "Whether or not the ballista will verify it won't hit other things before shooting.", advanced: true);
			ServerConfigsLocked = BindServerConfig("Server", "ServerConfigsLocked", value: false, "Server synced configs are not editable.", advanced: true);
			EnableDebugMode = Config.Bind<bool>("Client config", "EnableDebugMode", false, new ConfigDescription("Enables Debug logging for Valheim Fortress.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdvanced = true
			} }));
			EnableTurretDebugMode = Config.Bind<bool>("Client config", "EnableTurretDebugMode", false, new ConfigDescription("Enables debug mode for turrets, this can be noisy.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdvanced = true
			} }));
		}
	}
	internal class VFLocations
	{
		public VFLocations(AssetBundle EmbeddedResourceBundle, VFConfig cfg)
		{
			AddWildShrineLocationWithWorldGen(cfg, EmbeddedResourceBundle.LoadAsset<GameObject>("Assets/Custom/Locations/VFortress/VF_wild_shrine_green1.prefab"), (Biome)1);
			AddWildShrineLocationWithWorldGen(cfg, EmbeddedResourceBundle.LoadAsset<GameObject>("Assets/Custom/Locations/VFortress/VF_wild_shrine_blue1.prefab"), (Biome)8);
			AddWildShrineLocationWithWorldGen(cfg, EmbeddedResourceBundle.LoadAsset<GameObject>("Assets/Custom/Locations/VFortress/VF_wild_shrine_green2.prefab"), (Biome)2);
			AddWildShrineLocationWithWorldGen(cfg, EmbeddedResourceBundle.LoadAsset<GameObject>("Assets/Custom/Locations/VFortress/VF_wild_shrine_blue2.prefab"), (Biome)4);
			AddWildShrineLocationWithWorldGen(cfg, EmbeddedResourceBundle.LoadAsset<GameObject>("Assets/Custom/Locations/VFortress/VF_wild_shrine_yellow1.prefab"), (Biome)16);
			AddWildShrineLocationWithWorldGen(cfg, EmbeddedResourceBundle.LoadAsset<GameObject>("Assets/Custom/Locations/VFortress/VF_wild_shrine_purple1.prefab"), (Biome)512);
		}

		public void AddWildShrineLocationWithWorldGen(VFConfig cfg, GameObject prefab, Biome biome)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Expected O, but got Unknown
			if (!VFConfig.BindServerConfig("Wild Shrines", ((Object)prefab).name + "-Enable", value: true, "Enable/Disable the " + ((Object)prefab).name + " wildshrine.").Value)
			{
				if (VFConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo((object)("Skipped loading location " + ((Object)prefab).name));
				}
				return;
			}
			prefab.AddComponent<WildShrine>();
			prefab.AddComponent<Spawner>();
			LocationConfig val = new LocationConfig();
			val.Biome = biome;
			val.Quantity = VFConfig.NumberOfEachWildShrine.Value;
			val.Priotized = false;
			val.ExteriorRadius = 5f;
			val.SlopeRotation = true;
			val.MinAltitude = 1f;
			val.ClearArea = true;
			val.RandomRotation = false;
			val.MinDistanceFromSimilar = VFConfig.DistanceBetweenShrines.Value;
			ZoneManager.Instance.AddCustomLocation(new CustomLocation(prefab, true, val));
		}
	}
}
namespace ValheimFortress.Defenses
{
	public class VFTurret : MonoBehaviour, Hoverable, IPieceMarker
	{
		private static float m_turnRate = 80f;

		private static float m_horizontalAngle = 85f;

		private static float m_hitNoise = 10f;

		private static float m_shootWhenAimDiff = 0.99f;

		private static float m_predictionModifier = 1f;

		private static float m_updateTargetIntervalNear = 2f;

		private static float m_updateTargetIntervalFar = 8f;

		private static float m_aimDiffToTarget = -1f;

		public static float m_markerHideTime = 0.5f;

		public float m_viewDistance = VFConfig.BallistaRange.Value;

		public float m_attackCooldown = VFConfig.BallistaCooldownTime.Value;

		public float m_ammo_accuracy = VFConfig.BallistaAmmoAccuracyPenalty.Value;

		private static int max_ticks_between_target_cache_update = VFConfig.BallistaTargetUpdateCacheInterval.Value;

		private static LayerMask lmsk;

		private GameObject m_Projectile;

		public ItemData m_Ammo;

		private GameObject m_shootEffect;

		private GameObject m_reloadEffect;

		private GameObject m_newTargetEffect;

		private GameObject m_lostTargetEffect;

		private ZNetView m_nview;

		private Character m_target = null;

		private GameObject turretBodyArmed;

		private GameObject turretBodyUnarmed;

		private GameObject turretBodyArmedBolt;

		private GameObject turretBody;

		private GameObject turretNeck;

		private GameObject eye;

		private CircleProjector areaMarker;

		private Quaternion m_baseBodyRotation;

		private Quaternion m_baseNeckRotation;

		private List<Character> nearby_targets = new List<Character>();

		private int update_target_cache_interval = 0;

		private int current_cache_check_tick = 0;

		private bool m_haveTarget = false;

		private float m_updateTargetTimer = 0f;

		private float m_scan = 0f;

		private float m_noTargetScanRate = 12f;

		private ZDOIDZNetProperty target { get; set; }

		protected void Awake()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			m_nview = ((Component)this).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)m_nview))
			{
				target = new ZDOIDZNetProperty("VFTurret_Target", m_nview, ZDOID.None);
			}
			m_updateTargetTimer = Random.Range(0f, m_updateTargetIntervalNear);
			if (Object.op_Implicit((Object)(object)m_nview))
			{
				GameObject gameObject = ((Component)((Component)this).transform.Find("New")).gameObject;
				GameObject gameObject2 = ((Component)gameObject.transform.Find("Base")).gameObject;
				GameObject gameObject3 = ((Component)gameObject.transform.Find("NeckRotation")).gameObject;
				GameObject gameObject4 = ((Component)gameObject3.transform.GetChild(0)).gameObject;
				turretNeck = gameObject3;
				m_baseNeckRotation = turretNeck.transform.localRotation;
				GameObject val = (turretBody = ((Component)gameObject.transform.Find("BodyRotation")).gameObject);
				m_baseBodyRotation = turretBody.transform.localRotation;
				turretBodyArmed = ((Component)val.transform.Find("Body")).gameObject;
				turretBodyUnarmed = ((Component)val.transform.Find("Body_Unarmed")).gameObject;
				turretBodyArmedBolt = ((Component)val.transform.Find("Bolt_Black_Metal")).gameObject;
				eye = ((Component)val.transform.Find("Eye")).gameObject;
				m_Projectile = PrefabManager.Instance.GetPrefab("TurretBolt");
				m_Ammo = m_Projectile.GetComponent<ItemDrop>().m_itemData;
				m_Ammo.m_shared.m_damages.m_pierce = VFConfig.BallistaDamage.Value;
				areaMarker = ((Component)((Component)this).transform.Find("AreaMarker")).gameObject.GetComponent<CircleProjector>();
				m_shootEffect = PrefabManager.Instance.GetPrefab("fx_turret_fire");
				m_reloadEffect = PrefabManager.Instance.GetPrefab("fx_turret_reload");
				m_newTargetEffect = PrefabManager.Instance.GetPrefab("fx_turret_newtarget");
				m_lostTargetEffect = PrefabManager.Instance.GetPrefab("fx_turret_notarget");
				m_noTargetScanRate = Random.Range(8, 16);
				update_target_cache_interval = Random.Range(1, (int)VFConfig.BallistaTargetUpdateCacheInterval.Value);
				lmsk = LayerMask.op_Implicit(LayerMask.op_Implicit(lmsk) | 1);
				lmsk = LayerMask.op_Implicit(LayerMask.op_Implicit(lmsk) | 2);
				lmsk = LayerMask.op_Implicit(LayerMask.op_Implicit(lmsk) | 4);
				lmsk = LayerMask.op_Implicit(~LayerMask.op_Implicit(lmsk));
			}
		}

		private void FixedUpdate()
		{
			float fixedDeltaTime = Time.fixedDeltaTime;
			UpdateMarker();
			if (m_nview.IsValid())
			{
				ConnectTargetIfSetAndInRange();
				UpdateTurretRotation(fixedDeltaTime);
				if (m_nview.IsOwner() && !IsCoolingDown())
				{
					TurretRearmAnimate();
					UpdateTarget(fixedDeltaTime);
					ShootProjectile(fixedDeltaTime);
				}
			}
		}

		private void TurretRearmAnimate()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!turretBodyArmed.activeSelf)
			{
				Object.Instantiate<GameObject>(m_reloadEffect, turretBodyArmed.transform.position, turretBodyArmed.transform.rotation);
				turretBodyArmed.SetActive(true);
				turretBodyArmedBolt.SetActive(true);
				turretBodyUnarmed.SetActive(false);
			}
		}

		private void UpdateTurretRotation(float fixedDeltaTime)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: 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_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: 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_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_030b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0323: 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_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val2;
			Quaternion rotation;
			if (Object.op_Implicit((Object)(object)m_target))
			{
				float num = Vector2.Distance(Vector2.op_Implicit(((Component)m_target).transform.position), Vector2.op_Implicit(eye.transform.position)) / (m_Ammo.m_shared.m_attack.m_projectileVel * 2f);
				Vector3 val = m_target.GetVelocity() * num * m_predictionModifier;
				val2 = ((Component)m_target).transform.position + val - turretBody.transform.position;
				ref float y = ref val2.y;
				float num2 = y;
				CapsuleCollider componentInChildren = ((Component)m_target).GetComponentInChildren<CapsuleCollider>();
				y = num2 + ((componentInChildren != null) ? (componentInChildren.height / 2f) : 1f);
			}
			else
			{
				m_scan += fixedDeltaTime;
				if (m_scan > m_noTargetScanRate * 2f)
				{
					m_scan = 0f;
				}
				rotation = ((Component)this).transform.rotation;
				val2 = Quaternion.Euler(0f, ((Quaternion)(ref rotation)).eulerAngles.y + (float)((m_scan - m_noTargetScanRate > 0f) ? 1 : (-1)) * (m_horizontalAngle / 2f), 0f) * Vector3.forward;
			}
			((Vector3)(ref val2)).Normalize();
			Quaternion val3 = Quaternion.LookRotation(val2, Vector3.up);
			Vector3 eulerAngles = ((Quaternion)(ref val3)).eulerAngles;
			rotation = ((Component)this).transform.rotation;
			float y2 = ((Quaternion)(ref rotation)).eulerAngles.y;
			eulerAngles.y -= y2;
			if (m_horizontalAngle >= 0f)
			{
				float num3 = eulerAngles.y;
				if (num3 > 180f)
				{
					num3 -= 360f;
				}
				else if (num3 < -180f)
				{
					num3 += 360f;
				}
				if (num3 > m_horizontalAngle)
				{
					((Vector3)(ref eulerAngles))..ctor(eulerAngles.x, m_horizontalAngle + y2, eulerAngles.z);
					((Quaternion)(ref val3)).eulerAngles = eulerAngles;
				}
				else if (num3 < 0f - m_horizontalAngle)
				{
					((Vector3)(ref eulerAngles))..ctor(eulerAngles.x, 0f - m_horizontalAngle + y2, eulerAngles.z);
					((Quaternion)(ref val3)).eulerAngles = eulerAngles;
				}
			}
			Quaternion val4 = Quaternion.RotateTowards(turretBody.transform.rotation, val3, m_turnRate * fixedDeltaTime);
			turretBody.transform.rotation = m_baseBodyRotation * val4;
			Transform transform = turretNeck.transform;
			Quaternion baseNeckRotation = m_baseNeckRotation;
			rotation = turretBody.transform.rotation;
			float y3 = ((Quaternion)(ref rotation)).eulerAngles.y;
			rotation = turretBody.transform.rotation;
			transform.rotation = baseNeckRotation * Quaternion.Euler(0f, y3, ((Quaternion)(ref rotation)).eulerAngles.z);
			m_aimDiffToTarget = (m_haveTarget ? Math.Abs(Quaternion.Dot(val4, val3)) : (-1f));
		}

		private void UpdateTarget(float dt)
		{
			//IL_0042: 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_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: 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_00e0: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: 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)
			if (Object.op_Implicit((Object)(object)m_target))
			{
				return;
			}
			m_updateTargetTimer -= dt;
			if (m_updateTargetTimer <= 0f)
			{
				bool flag = IsCharacterInRangeAndNotPlayer(((Component)this).transform.position, 40f, Character.GetAllCharacters());
				m_updateTargetTimer = (flag ? m_updateTargetIntervalNear : m_updateTargetIntervalFar);
				Character val = selectTarget(flag);
				if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)m_target)
				{
					if (Object.op_Implicit((Object)(object)val))
					{
						Object.Instantiate<GameObject>(m_newTargetEffect, ((Component)this).transform.position, ((Component)this).transform.rotation);
					}
					else
					{
						Object.Instantiate<GameObject>(m_lostTargetEffect, ((Component)this).transform.position, ((Component)this).transform.rotation);
					}
					if (VFConfig.EnableTurretDebugMode.Value)
					{
						Logger.LogInfo((object)$"set target {val}");
					}
					target.Set(Object.op_Implicit((Object)(object)val) ? val.GetZDOID() : ZDOID.None);
					m_target = val;
					m_haveTarget = true;
				}
			}
			if (m_haveTarget && (!Object.op_Implicit((Object)(object)m_target) || m_target.IsDead()))
			{
				target.Set(ZDOID.None);
				m_haveTarget = false;
				m_scan = 0f;
				Object.Instantiate<GameObject>(m_lostTargetEffect, ((Component)this).transform.position, ((Component)this).transform.rotation);
			}
		}

		public static bool IsCharacterInRangeAndNotPlayer(Vector3 point, float range, List<Character> character_list)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			foreach (Character item in character_list)
			{
				if (!(Vector3.Distance(((Component)item).transform.position, point) < range) || item.IsPlayer() || (int)item.GetFaction() == 0 || item is Player)
				{
					continue;
				}
				return true;
			}
			return false;
		}

		private bool IsValidTarget(Character ptarget)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Invalid comparison between Unknown and I4
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ptarget == (Object)null)
			{
				return false;
			}
			if (ptarget.IsDead() || ptarget.IsTamed() || ptarget.IsPlayer())
			{
				return false;
			}
			if (!VFConfig.BallistaTargetsPassives.Value && (int)ptarget.GetFaction() == 1)
			{
				return false;
			}
			if ((int)ptarget.GetFaction() == 0 || ptarget is Player)
			{
				return false;
			}
			return true;
		}

		private Character selectTarget(bool character_in_range)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: 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_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			if (!character_in_range)
			{
				return null;
			}
			if (nearby_targets.Count == 0 || update_target_cache_interval == current_cache_check_tick)
			{
				if (VFConfig.EnableTurretDebugMode.Value)
				{
					Logger.LogInfo((object)"Updating Turret target cache.");
				}
				Character.GetCharactersInRange(((Component)this).transform.position, m_viewDistance, nearby_targets);
				nearby_targets.OrderBy((Character o) => Vector3.Distance(((Component)this).transform.position, ((Component)o).gameObject.transform.position));
			}
			List<Character> list = new List<Character>(nearby_targets);
			if (max_ticks_between_target_cache_update <= current_cache_check_tick)
			{
				current_cache_check_tick = 0;
			}
			else
			{
				current_cache_check_tick++;
			}
			Character val = null;
			if (VFConfig.EnableTurretDebugMode.Value)
			{
				Logger.LogInfo((object)$"checking targets {list.Count}");
			}
			RaycastHit val3 = default(RaycastHit);
			foreach (Character item in list)
			{
				if (!IsValidTarget(item))
				{
					nearby_targets.Remove(item);
					continue;
				}
				BaseAI baseAI = item.GetBaseAI();
				if (baseAI.IsSleeping())
				{
					continue;
				}
				float num = Vector3.Distance(((Component)this).transform.position, ((Component)item).transform.position);
				if (num < m_viewDistance)
				{
					Vector3 val2 = ((Component)item).transform.position - eye.transform.position;
					Vector3 normalized = ((Vector3)(ref val2)).normalized;
					bool flag = Physics.Raycast(eye.transform.position, normalized, ref val3, m_viewDistance, LayerMask.op_Implicit(lmsk));
					float num2 = Vector3.Distance(eye.transform.position, ((Component)item).transform.position);
					bool flag2 = ((RaycastHit)(ref val3)).distance > num2 - 2f;
					if (VFConfig.EnableTurretDebugMode.Value)
					{
						Logger.LogInfo((object)$"TargetCheck: distance to target: {num2}, raycast distance test: {((RaycastHit)(ref val3)).distance}, can hit distance: {flag2}");
					}
					if (flag2)
					{
						val = item;
						break;
					}
				}
			}
			if ((Object)(object)val != (Object)null && VFConfig.EnableTurretDebugMode.Value)
			{
				Logger.LogInfo((object)$"Selected target: {val}");
			}
			return val;
		}

		public void ShootProjectile(float dt)
		{
			//IL_004a: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Expected O, but got Unknown
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Unknown result type (might be due to invalid IL or missing references)
			//IL_037e: Unknown result type (might be due to invalid IL or missing references)
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)m_target) || !(m_aimDiffToTarget > m_shootWhenAimDiff) || IsCoolingDown())
			{
				return;
			}
			if (VFConfig.BallistaEnableShotSafetyCheck.Value)
			{
				RaycastHit val = default(RaycastHit);
				bool flag = Physics.Raycast(eye.transform.position, eye.transform.forward, ref val, m_viewDistance, LayerMask.op_Implicit(lmsk));
				Vector3 position = eye.transform.position;
				position.z += 0.5f;
				float num = Vector3.Distance(position, ((Component)m_target).transform.position);
				bool flag2 = ((RaycastHit)(ref val)).distance > num - 2f;
				if (VFConfig.EnableTurretDebugMode.Value)
				{
					Logger.LogInfo((object)$" distance to target: {num}, raycast distance test: {((RaycastHit)(ref val)).distance}, can hit distance: {flag2} hit bool: {flag}");
				}
				if (!flag2 || !flag)
				{
					return;
				}
			}
			if (VFConfig.EnableTurretDebugMode.Value)
			{
				Logger.LogInfo((object)$"Turret target status:{!Object.op_Implicit((Object)(object)m_target)} aimdiff:{m_aimDiffToTarget} > {m_shootWhenAimDiff} ({!(m_aimDiffToTarget > m_shootWhenAimDiff)}) cooldown:{IsCoolingDown()}");
			}
			Object.Instantiate<GameObject>(m_shootEffect, turretBodyArmed.transform.position, eye.transform.rotation);
			m_nview.GetZDO().Set("lastAttack", (float)ZNet.instance.GetTimeSeconds());
			Vector3 forward = eye.transform.forward;
			Vector3 val2 = Vector3.Cross(forward, Vector3.up);
			Quaternion val3 = Quaternion.AngleAxis(Random.Range(0f - m_ammo_accuracy, m_ammo_accuracy), Vector3.up);
			forward = Quaternion.AngleAxis(Random.Range(0f - m_ammo_accuracy, m_ammo_accuracy), val2) * forward;
			forward = val3 * forward;
			GameObject val4 = Object.Instantiate<GameObject>(m_Ammo.m_shared.m_attack.m_attackProjectile, eye.transform.position, eye.transform.rotation);
			HitData val5 = new HitData();
			val5.m_pushForce = m_Ammo.m_shared.m_attackForce;
			val5.m_backstabBonus = m_Ammo.m_shared.m_backstabBonus;
			val5.m_staggerMultiplier = m_Ammo.m_shared.m_attack.m_staggerMultiplier;
			((DamageTypes)(ref val5.m_damage)).Add(m_Ammo.GetDamage(), 1);
			val5.m_blockable = m_Ammo.m_shared.m_blockable;
			val5.m_dodgeable = m_Ammo.m_shared.m_dodgeable;
			val5.m_skill = m_Ammo.m_shared.m_skillType;
			IProjectile component = val4.GetComponent<IProjectile>();
			if (component != null)
			{
				component.Setup((Character)null, forward * (m_Ammo.m_shared.m_attack.m_projectileVel * 3f), m_hitNoise, val5, (ItemData)null, m_Ammo);
			}
			turretBodyArmed.SetActive(false);
			turretBodyArmedBolt.SetActive(false);
			turretBodyUnarmed.SetActive(true);
		}

		public bool IsCoolingDown()
		{
			if (!m_nview.IsValid())
			{
				return false;
			}
			return (double)(m_nview.GetZDO().GetFloat("lastAttack", 0f) + m_attackCooldown) > ZNet.instance.GetTimeSeconds();
		}

		public string GetHoverText()
		{
			if (!m_nview.IsValid())
			{
				return "";
			}
			return Localization.instance.Localize("$piece_vfturret");
		}

		public string GetHoverName()
		{
			return Localization.instance.Localize("$piece_vfturret");
		}

		private void ConnectTargetIfSetAndInRange()
		{
			//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_0037: 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_0058: 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)
			if (!(target.Get() != ZDOID.None) || m_haveTarget)
			{
				return;
			}
			GameObject val = ZNetScene.instance.FindInstance(target.Get());
			if (Object.op_Implicit((Object)(object)val))
			{
				if (Vector3.Distance(eye.transform.position, val.transform.position) > m_viewDistance)
				{
					Character component = val.GetComponent<Character>();
					if (component != null)
					{
						m_target = component;
						m_haveTarget = true;
					}
				}
			}
			else
			{
				target.ForceSet(ZDOID.None);
				m_target = null;
				m_haveTarget = false;
				m_scan = 0f;
			}
		}

		private void OnDestroyed()
		{
			((Component)this).GetComponent<WearNTear>().m_onDestroyed();
		}

		public void ShowHoverMarker()
		{
			ShowBuildMarker();
		}

		public void ShowBuildMarker()
		{
			if (!Object.op_Implicit((Object)(object)areaMarker))
			{
				areaMarker.m_radius = m_viewDistance;
				((Component)areaMarker).gameObject.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)areaMarker))
			{
				((Component)areaMarker).gameObject.SetActive(true);
				((MonoBehaviour)this).CancelInvoke("HideMarker");
				((MonoBehaviour)this).Invoke("HideMarker", m_markerHideTime);
			}
		}

		private void UpdateMarker()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)areaMarker) && ((Behaviour)areaMarker).isActiveAndEnabled)
			{
				CircleProjector obj = areaMarker;
				Quaternion rotation = ((Component)this).transform.rotation;
				obj.m_start = ((Quaternion)(ref rotation)).eulerAngles.y - m_horizontalAngle;
				areaMarker.m_turns = m_horizontalAngle * 2f / 360f;
			}
		}

		private void HideMarker()
		{
			if (Object.op_Implicit((Object)(object)areaMarker))
			{
				((Component)areaMarker).gameObject.SetActive(false);
			}
		}
	}
}
namespace ValheimFortress.common
{
	public class JotunnPiece
	{
		private Dictionary<string, string> PieceMetadata;

		private Dictionary<string, Tuple<int, bool>> RecipeData;

		private Dictionary<string, bool> PieceToggles;

		private Dictionary<string, Tuple<int, bool>> UpdatedRecipeData = new Dictionary<string, Tuple<int, bool>>();

		private GameObject ScenePrefab;

		private GameObject PiecePrefab;

		private Sprite PieceSprite;

		private ConfigEntry<bool> EnabledVFConfig;

		private ConfigEntry<string> RecipeVFConfig;

		private ConfigEntry<string> BuiltAt;

		public JotunnPiece(Dictionary<string, string> metadata, Dictionary<string, bool> pieceToggles, Dictionary<string, Tuple<int, bool>> recipedata)
		{
			PieceMetadata = metadata;
			PieceToggles = pieceToggles;
			RecipeData = recipedata;
			PieceMetadata["short_item_name"] = string.Join("", metadata["name"].Split((string[]?)null, StringSplitOptions.RemoveEmptyEntries));
			if (!PieceToggles.ContainsKey("enabled"))
			{
				PieceToggles.Add("enabled", value: true);
			}
			PiecePrefab = ValheimFortress.EmbeddedResourceBundle.LoadAsset<GameObject>("Assets/Custom/Pieces/" + PieceMetadata["catagory"] + "/" + PieceMetadata["prefab"] + ".prefab");
			PieceSprite = ValheimFortress.EmbeddedResourceBundle.LoadAsset<Sprite>("Assets/Custom/Icons/" + PieceMetadata["sprite"] + ".png");
			InitPieceConfigs();
			InitialPieceSetup();
			if (PieceMetadata["prefab"] == "VFpiece_turret")
			{
				PiecePrefab.AddComponent<VFTurret>();
				VFConfig.BallistaDamage.SettingChanged += BallistaDamageChange;
				VFConfig.BallistaRange.SettingChanged += BallistaRange_SettingChanged;
				VFConfig.BallistaAmmoAccuracyPenalty.SettingChanged += BallistaAmmoAccuracyPenalty_SettingChanged;
				VFConfig.BallistaCooldownTime.SettingChanged += BallistaCooldownTime_SettingChanged;
			}
			if (PieceMetadata["prefab"] == "VFshrine_of_challenge")
			{
				PiecePrefab.AddComponent<ChallengeShrine>();
				PiecePrefab.AddComponent<ChallengeShrineUI>();
				PiecePrefab.AddComponent<Spawner>();
			}
			if (PieceMetadata["prefab"] == "VFshine_of_gladiator")
			{
				PiecePrefab.AddComponent<ArenaShrine>();
				PiecePrefab.AddComponent<ArenaShrineUI>();
				PiecePrefab.AddComponent<Spawner>();
			}
			PrefabManager.OnPrefabsRegistered += SetSceneParentPrefab;
		}

		private void BallistaCooldownTime_SettingChanged(object sender, EventArgs e)
		{
			foreach (GameObject item in findPrefabInScene())
			{
				if (VFConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo((object)("Updating attack cooldown duration on " + ((Object)item).name));
				}
				item.GetComponent<VFTurret>().m_attackCooldown = VFConfig.BallistaCooldownTime.Value;
			}
		}

		private void BallistaAmmoAccuracyPenalty_SettingChanged(object sender, EventArgs e)
		{
			foreach (GameObject item in findPrefabInScene())
			{
				if (VFConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo((object)("Updating accuracy on " + ((Object)item).name));
				}
				item.GetComponent<VFTurret>().m_ammo_accuracy = VFConfig.BallistaAmmoAccuracyPenalty.Value;
			}
		}

		private void BallistaRange_SettingChanged(object sender, EventArgs e)
		{
			foreach (GameObject item in findPrefabInScene())
			{
				if (VFConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo((object)("Updating range on " + ((Object)item).name));
				}
				item.GetComponent<VFTurret>().m_viewDistance = VFConfig.BallistaRange.Value;
			}
		}

		private void BallistaDamageChange(object sender, EventArgs e)
		{
			foreach (GameObject item in findPrefabInScene())
			{
				if (VFConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo((object)("Updating dmg on " + ((Object)item).name));
				}
				item.GetComponent<VFTurret>().m_Ammo.m_shared.m_damages.m_pierce = VFConfig.BallistaDamage.Value;
			}
		}

		private IEnumerable<GameObject> findPrefabInScene()
		{
			IEnumerable<GameObject> enumerable = from obj in Resources.FindObjectsOfTypeAll<GameObject>()
				where ((Object)obj).name.StartsWith(PieceMetadata["prefab"])
				select obj;
			if (VFConfig.EnableDebugMode.Value)
			{
				Logger.LogInfo((object)$"Found in scene objects: {enumerable.Count()}");
			}
			return enumerable;
		}

		private void InitialPieceSetup()
		{
			//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)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Expected O, but got Unknown
			CreateAndUpdateRecipe();
			RequirementConfig[] array = (RequirementConfig[])(object)new RequirementConfig[UpdatedRecipeData.Count];
			int num = 0;
			foreach (KeyValuePair<string, Tuple<int, bool>> updatedRecipeDatum in UpdatedRecipeData)
			{
				array[num] = new RequirementConfig
				{
					Item = updatedRecipeDatum.Key,
					Amount = updatedRecipeDatum.Value.Item1,
					Recover = updatedRecipeDatum.Value.Item2
				};
				num++;
			}
			PieceConfig val = new PieceConfig
			{
				CraftingStation = (PieceMetadata["requiredBench"] ?? ""),
				PieceTable = PieceTables.Hammer,
				Category = PieceMetadata["catagory"],
				Icon = PieceSprite,
				Requirements = array
			};
			PieceManager.Instance.AddPiece(new CustomPiece(PiecePrefab, true, val));
		}

		private void SetSceneParentPrefab()
		{
			IEnumerable<GameObject> source = from obj in Resources.FindObjectsOfTypeAll<GameObject>()
				where ((Object)obj).name == PieceMetadata["prefab"]
				select obj;
			if (VFConfig.EnableDebugMode.Value)
			{
				Logger.LogInfo((object)string.Format("Found {0} scene parent objects: {1}", PieceMetadata["prefab"], source.Count()));
			}
			ScenePrefab = source.First();
		}

		private void CreateAndUpdateRecipe()
		{
			string text = "";
			foreach (KeyValuePair<string, Tuple<int, bool>> recipeDatum in RecipeData)
			{
				if (text.Length > 0)
				{
					text += "|";
				}
				text += $"{recipeDatum.Key},{recipeDatum.Value.Item1},{recipeDatum.Value.Item2}";
			}
			RecipeVFConfig = VFConfig.BindServerConfig(PieceMetadata["catagory"] + " - " + PieceMetadata["name"], PieceMetadata["short_item_name"] + "-recipe", text, "Recipe to craft and upgrade costs. Find item ids: https://valheim.fandom.com/wiki/Item_IDs, at most 4 costs. Format: resouce_id,craft_cost,upgrade_cost eg: Wood,8,2|Iron,12,4|LeatherScraps,4,0", advanced: true);
			if (!PieceRecipeVFConfigUpdater(RecipeVFConfig.Value, startup: true))
			{
				Logger.LogWarning((object)(PieceMetadata["name"] + " has an invalid recipe. The default will be used instead."));
				PieceRecipeVFConfigUpdater(text, startup: true);
			}
			RecipeVFConfig.SettingChanged += BuildRecipeChanged_SettingChanged;
		}

		private void InitPieceConfigs()
		{
			EnabledVFConfig = VFConfig.BindServerConfig(PieceMetadata["catagory"] + " - " + PieceMetadata["name"], PieceMetadata["short_item_name"] + "-enabled", PieceToggles["enabled"], "Enable/Disable the " + PieceMetadata["name"] + ".");
			PieceToggles["enabled"] = EnabledVFConfig.Value;
			EnabledVFConfig.SettingChanged += BuildRecipeChanged_SettingChanged;
			BuiltAt = VFConfig.BindServerConfig(PieceMetadata["catagory"] + " - " + PieceMetadata["name"], PieceMetadata["short_item_name"] + "-requiredBench", PieceMetadata["requiredBench"], "The table required to allow building this piece, eg: 'forge', 'piece_workbench', 'blackforge', 'piece_artisanstation'.");
			PieceMetadata["requiredBench"] = BuiltAt.Value;
			BuiltAt.SettingChanged += RequiredBench_SettingChanged;
		}

		private void BuildRecipeChanged_SettingChanged(object sender, EventArgs e)
		{
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: 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_0187: Expected O, but got Unknown
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Expected O, but got Unknown
			if (sender.GetType() == typeof(ConfigEntry<string>))
			{
				ConfigEntry<string> val = (ConfigEntry<string>)sender;
				if (VFConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo((object)("Recieved new piece VFConfig " + val.Value));
				}
				if (!PieceRecipeVFConfigUpdater(val.Value))
				{
					return;
				}
			}
			RequirementConfig[] array = (RequirementConfig[])(object)new RequirementConfig[UpdatedRecipeData.Count];
			int num = 0;
			if (VFConfig.EnableDebugMode.Value)
			{
				Logger.LogInfo((object)"Validating and building requirementsVFConfig");
			}
			foreach (KeyValuePair<string, Tuple<int, bool>> updatedRecipeDatum in UpdatedRecipeData)
			{
				if ((Object)(object)PrefabManager.Instance.GetPrefab(updatedRecipeDatum.Key) == (Object)null)
				{
					if (VFConfig.EnableDebugMode.Value)
					{
						Logger.LogInfo((object)(updatedRecipeDatum.Key + " is not a valid prefab, skipping recipe update."));
					}
					return;
				}
				if (VFConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo((object)$"Checking entry {updatedRecipeDatum.Key} c:{updatedRecipeDatum.Value.Item1} r:{updatedRecipeDatum.Value.Item2}");
				}
				array[num] = new RequirementConfig
				{
					Item = updatedRecipeDatum.Key,
					Amount = updatedRecipeDatum.Value.Item1,
					Recover = updatedRecipeDatum.Value.Item2
				};
				num++;
			}
			if (PieceToggles["enabled"])
			{
				if (VFConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo((object)"Updating Piece.");
				}
				Requirement[] array2 = (Requirement[])(object)new Requirement[UpdatedRecipeData.Count];
				int num2 = 0;
				RequirementConfig[] array3 = array;
				foreach (RequirementConfig val2 in array3)
				{
					Requirement val3 = new Requirement();
					GameObject prefab = PrefabManager.Instance.GetPrefab(val2.Item.Replace("JVLmock_", ""));
					val3.m_resItem = ((prefab != null) ? prefab.GetComponent<ItemDrop>() : null);
					val3.m_amount = val2.Amount;
					val3.m_recover = val2.Recover;
					array2[num2] = val3;
					num2++;
				}
				if (VFConfig.EnableDebugMode.Value)
				{
					Logger.LogInfo((object)$"Fixed mock requirements {array2.Length}.");
				}
				ScenePrefab.GetComponent<Piece>().m_resources = array2;
				{
					foreach (GameObject item in findPrefabInScene())
					{
						if (VFConfig.EnableDebugMode.Value)
						{
							Logger.LogInfo((object)("Updating requirements on " + ((Object)item).name));
						}
						item.GetComponent<Piece>().m_resour