Decompiled source of ImpactfulSkills v0.18.0

plugins/ImpactfulSkills.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using ImpactfulSkills.common;
using ImpactfulSkills.compatibility;
using ImpactfulSkills.modules;
using ImpactfulSkills.modules.Multiplant;
using ImpactfulSkills.patches;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using SimpleJson;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ImpactfulSkills")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ImpactfulSkills")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.18.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.18.0.0")]
namespace ImpactfulSkills
{
	internal class ValConfig
	{
		public static ConfigFile cfg;

		public static ConfigEntry<bool> EnableDebugMode;

		public const string cfgFolder = "ImpactfulSkills";

		public static bool AOEFeaturesEnabled = true;

		public static ConfigEntry<KeyboardShortcut> AOEToggleHotkey;

		public static ConfigEntry<bool> EnableWoodcutting;

		public static ConfigEntry<float> WoodCuttingDmgMod;

		public static ConfigEntry<float> WoodCuttingLootFactor;

		public static ConfigEntry<bool> EnableMining;

		public static ConfigEntry<float> MiningDmgMod;

		public static ConfigEntry<float> MiningLootFactor;

		public static ConfigEntry<float> MiningAOERange;

		public static ConfigEntry<float> MiningAOELevel;

		public static ConfigEntry<bool> EnableMiningAOE;

		public static ConfigEntry<bool> EnableMiningRockBreaker;

		public static ConfigEntry<bool> ProtectLeviathansWhenMined;

		public static ConfigEntry<float> RockBreakerMaxChance;

		public static ConfigEntry<int> RockBreakerRequiredLevel;

		public static ConfigEntry<float> RockBreakerDamage;

		public static ConfigEntry<int> MinehitsPerInterval;

		public static ConfigEntry<float> ChanceForAOEOnHit;

		public static ConfigEntry<bool> SkillLevelBonusEnabledForMiningDropChance;

		public static ConfigEntry<bool> ChanceForAOEOnHitScalesWithSkill;

		public static ConfigEntry<bool> EnableMiningCritHit;

		public static ConfigEntry<int> RequiredLevelForMiningCrit;

		public static ConfigEntry<float> ChanceForMiningCritHit;

		public static ConfigEntry<float> CriticalHitDmgMult;

		public static ConfigEntry<bool> SkipNonRockDropIncreases;

		public static ConfigEntry<bool> ReducedChanceDropsForLowAmountDrops;

		public static ConfigEntry<float> DistanceMiningDropMultiplierChecks;

		public static ConfigEntry<float> RockbreakerSafetyResetTimeout;

		public static ConfigEntry<string> SkipNonRockDropPrefabs;

		public static ConfigEntry<bool> FractionalDropsAsChance;

		public static ConfigEntry<bool> EnableStealth;

		public static ConfigEntry<float> SneakSpeedFactor;

		public static ConfigEntry<float> SneakNoiseReductionLevel;

		public static ConfigEntry<float> SneakNoiseReductionFactor;

		public static ConfigEntry<bool> EnableSneakBonusDamage;

		public static ConfigEntry<int> SneakBackstabBonusLevel;

		public static ConfigEntry<float> SneakBackstabBonusFactor;

		public static ConfigEntry<bool> EnableRun;

		public static ConfigEntry<float> RunSpeedFactor;

		public static ConfigEntry<bool> EnableJump;

		public static ConfigEntry<float> JumpHeightFactor;

		public static ConfigEntry<float> JumpHeightRequiredLevel;

		public static ConfigEntry<bool> EnableFallDamageReduction;

		public static ConfigEntry<float> FallDamageReductionFactor;

		public static ConfigEntry<float> FallDamageReductionRequiredLevel;

		public static ConfigEntry<float> FallDamageHeightBonus;

		public static ConfigEntry<float> FallDamageHeightRequiredLevel;

		public static ConfigEntry<bool> EnableFallDamageHeightBonus;

		public static ConfigEntry<bool> EnableAnimalWhisper;

		public static ConfigEntry<float> AnimalTamingSpeedFactor;

		public static ConfigEntry<float> TamedAnimalLootIncreaseFactor;

		public static ConfigEntry<float> AnimalHandlingLootRange;

		public static ConfigEntry<bool> AnimalHandlingFractionalDropsAsChance;

		public static ConfigEntry<int> BetterBeesLevel;

		public static ConfigEntry<bool> EnableBeeBonuses;

		public static ConfigEntry<int> BeeBiomeUnrestrictedLevel;

		public static ConfigEntry<bool> EnableBeeBiomeUnrestricted;

		public static ConfigEntry<float> BeeHoneyOutputIncreaseBySkill;

		public static ConfigEntry<float> BeeHarvestXP;

		public static ConfigEntry<float> AnimalBreedingXP;

		public static ConfigEntry<float> AnimalBreedingXPRange;

		public static ConfigEntry<bool> EnableGathering;

		public static ConfigEntry<bool> EnableGatheringAOE;

		public static ConfigEntry<float> GatheringRangeFactor;

		public static ConfigEntry<int> FarmingRangeRequiredLevel;

		public static ConfigEntry<string> GatheringLuckLevels;

		public static ConfigEntry<string> GatheringDisallowedItems;

		public static ConfigEntry<bool> EnableFarmingBiomeUnrestricted;

		public static ConfigEntry<int> FarmingBiomeUnrestrictedLevel;

		public static ConfigEntry<bool> EnableFarmingMultiPlant;

		public static ConfigEntry<int> FarmingMultiplantRequiredLevel;

		public static ConfigEntry<int> FarmingMultiplantMaxPlantedAtOnce;

		public static ConfigEntry<int> FarmingMultiplantColumnCount;

		public static ConfigEntry<int> FarmingMultiplantCountIncrement;

		public static ConfigEntry<bool> FarmingMultiPlantSnapToExisting;

		public static ConfigEntry<float> FarmingMultiPlantBufferSpace;

		public static ConfigEntry<float> PlantingCostStaminaReduction;

		public static ConfigEntry<float> PlantingSnapDistance;

		public static ConfigEntry<float> PlantingAOEHarvestResetSafety;

		public static ConfigEntry<bool> PreferOtherPlantGrid;

		public static ConfigEntry<string> FarmingSnapStyle;

		public static ConfigEntry<bool> FarmingSnapPreferCardinal;

		public static ConfigEntry<bool> EnableSnappingToOtherPlants;

		public static ConfigEntry<float> FarmingMultiPlantDistanceBufferModifier;

		public static ConfigEntry<bool> FarmingMultiPlantPersistOrientation;

		public static ConfigEntry<bool> FarmingMultiPlantRandomRotation;

		public static ConfigEntry<bool> FarmingMultiPlantCenterRows;

		public static ConfigEntry<bool> FarmingMultiPlantCenterColumns;

		public static ConfigEntry<bool> EnableVoyager;

		public static ConfigEntry<int> VoyagerSkillXPCheckFrequency;

		public static ConfigEntry<float> VoyagerReduceCuttingStart;

		public static ConfigEntry<float> VoyagerCuttingMinAngle;

		public static ConfigEntry<float> VoyagerSailingSpeedFactor;

		public static ConfigEntry<float> VoyagerIncreaseExplorationRadius;

		public static ConfigEntry<float> VoyagerPaddleSpeedBonus;

		public static ConfigEntry<float> VoyagerPaddleSpeedBonusLevel;

		public static ConfigEntry<bool> EnableBoatDamageReduction;

		public static ConfigEntry<int> BoatDamageReductionLevel;

		public static ConfigEntry<float> VoyagerDamageReductionAmount;

		public static ConfigEntry<bool> VoyagerImpactResistance;

		public static ConfigEntry<int> VoyagerImpactResistanceLevel;

		public static ConfigEntry<bool> EnableFriendsRowSpeedBonus;

		public static ConfigEntry<float> MaxFriendsRowSpeedBonus;

		public static ConfigEntry<bool> EnableWeaponSkill;

		public static ConfigEntry<float> WeaponSkillStaminaReduction;

		public static ConfigEntry<float> WeaponSkillBowDrawStaminaCostReduction;

		public static ConfigEntry<float> WeaponSkillParryBonus;

		public static ConfigEntry<float> WeaponSkillEquipSpeedFactor;

		public static ConfigEntry<int> WeaponSkillEquipRequiredLevel;

		public static ConfigEntry<bool> EnableCooking;

		public static ConfigEntry<bool> EnableCookingDegradeReduction;

		public static ConfigEntry<bool> EnableCookingBonusItems;

		public static ConfigEntry<float> ChanceForCookingBonusItems;

		public static ConfigEntry<int> RequiredLevelForBonusCookingItems;

		public static ConfigEntry<int> CookingBonusItemMaxAmount;

		public static ConfigEntry<float> CookingBurnReduction;

		public static ConfigEntry<bool> EnableCookingStreak;

		public static ConfigEntry<float> CookingStreakBonusPerFood;

		public static ConfigEntry<float> CookingStreakMaxBonus;

		public static ConfigEntry<float> CookingStreakTimeout;

		public static ConfigEntry<bool> CookingStreakShowText;

		public static ConfigEntry<bool> EnableCookingEatXP;

		public static ConfigEntry<float> CookingEatXPPerFoodStat;

		public static ConfigEntry<bool> EnableHauling;

		public static ConfigEntry<bool> EnableCarryWeightBonus;

		public static ConfigEntry<float> HaulingMaxWeightBonus;

		public static ConfigEntry<bool> EnableHaulingCartMassReduction;

		public static ConfigEntry<float> HaulingCartMassReduction;

		public static ConfigEntry<float> HaulingXPRate;

		public static ConfigEntry<int> HaulingXPCheckInterval;

		public static ConfigEntry<float> HaulingXPMinDistance;

		public static ConfigEntry<bool> EnableHaulingCarryWeightXP;

		public static ConfigEntry<float> HaulingCarryWeightXPMinWeight;

		public static ConfigEntry<float> HaulingCarryWeightXPRate;

		public static ConfigEntry<float> HaulingMaxLoadRatio;

		public static ConfigEntry<bool> EnableBloodMagic;

		public static ConfigEntry<float> BloodMagicXPForShieldDamageRatio;

		public static ConfigEntry<float> BloodMagicXP;

		public static ConfigEntry<bool> EnableKnowledgeSharing;

		public static ConfigEntry<float> AnimalTamingSkillGainRate;

		public static ConfigEntry<float> VoyagerSkillGainRate;

		public static ConfigEntry<float> SharedKnowledgeSkillBonusRate;

		public static ConfigEntry<float> SharedKnowledgeCap;

		public static ConfigEntry<string> SharedKnowledgeIgnoreList;

		public static ConfigEntry<string> AdditionalSkillNames;

		public static ConfigEntry<bool> EnableCrafting;

		public static ConfigEntry<bool> EnableDurabilitySaves;

		public static ConfigEntry<bool> ScaleDurabilitySaveBySkillLevel;

		public static ConfigEntry<bool> EnableDurabilityLossPrevention;

		public static ConfigEntry<int> DurabilitySaveLevel;

		public static ConfigEntry<float> ChanceForDurabilityLossPrevention;

		public static ConfigEntry<int> CraftingMaxBonus;

		public static ConfigEntry<float> CraftingBonusChance;

		public static ConfigEntry<bool> EnableBonusItemCrafting;

		public static ConfigEntry<bool> EnableCraftBonusAsFraction;

		public static ConfigEntry<float> CraftBonusFractionOfCraftNumber;

		public static ConfigEntry<int> CraftingBonusCraftsLevel;

		public static ConfigEntry<bool> EnableMaterialReturns;

		public static ConfigEntry<int> CraftingMaterialReturnsLevel;

		public static ConfigEntry<float> MaxCraftingMaterialReturnPercent;

		public static ConfigEntry<float> ChanceForMaterialReturn;

		public static ConfigEntry<bool> ScaleCraftedEquipmentQuality;

		public static ConfigEntry<bool> EnableSwimming;

		public static ConfigEntry<int> SwimSpeedRequiredLevel;

		public static ConfigEntry<float> SwimmingSpeedFactor;

		public static ConfigEntry<bool> EnableSwimStaminaCostReduction;

		public static ConfigEntry<int> SwimStaminaReductionLevel;

		public static ConfigEntry<float> SwimStaminaCostReductionFactor;

		public static ConfigEntry<bool> EnableBlocking;

		public static ConfigEntry<int> BlockPowerRequiredLevel;

		public static ConfigEntry<float> BlockPowerFactor;

		public static ConfigEntry<int> BlockStaminaGainRequiredLevel;

		public static ConfigEntry<float> BlockStaminaGainFactor;

		public static ConfigEntry<bool> EnableParryStaminaGain;

		public static ConfigEntry<bool> EnableQualityIngredientScaling;

		public static ConfigEntry<float> QualityIngredientOutputMultiplier;

		public static ConfigEntry<bool> RestrictQualityScalingToFish;

		public static ConfigEntry<bool> ScaleNonStackingCraftOutputs;

		internal static CustomRPC SkillIncreaseXP;

		public ValConfig(ConfigFile cf)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0059: Expected O, but got Unknown
			cfg = cf;
			cfg.SaveOnConfigSet = true;
			CreateConfigValues(cf);
			Logger.setDebugLogging(EnableDebugMode.Value);
			SetupMainFileWatcher();
			SkillIncreaseXP = NetworkManager.Instance.AddRPC("ISKILL_XP_GRANT", new CoroutineHandler(OnServerReceiveXPRequest), new CoroutineHandler(OnClientReceiveXPGrant));
		}

		private void CreateConfigValues(ConfigFile Config)
		{
			//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_002c: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			EnableDebugMode = Config.Bind<bool>("Client config", "EnableDebugMode", false, new ConfigDescription("Enables Debug logging.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdvanced = true
			} }));
			EnableDebugMode.SettingChanged += Logger.enableDebugLogging;
			AOEToggleHotkey = BindClientConfig("Client config", "AOEToggleHotkey", new KeyboardShortcut((KeyCode)110, Array.Empty<KeyCode>()), "Hotkey to toggle AOE harvesting, AOE mining and AOE planting on/off.");
			EnableWoodcutting = BindServerConfig("Woodcutting", "EnableWoodcutting", value: true, "Enable woodcutting skill changes.");
			WoodCuttingDmgMod = BindServerConfig("Woodcutting", "WoodCuttingDmgMod", 1.2f, "How much skill levels impact your chop damage.");
			WoodCuttingLootFactor = BindServerConfig("Woodcutting", "WoodCuttingLootFactor", 3f, "How much the woodcutting skill provides additional loot. 1 is vanilla (no bonus), 2 is 2x the loot at level 100.", advanced: false, 1f, 10f);
			EnableMining = BindServerConfig("Mining", "EnableMining", value: true, "Enable mining skill changes.");
			MiningDmgMod = BindServerConfig("Mining", "MiningDmgMod", 1.2f, "How much your skill levels impact mining damage.");
			EnableMiningCritHit = BindServerConfig("Mining", "EnableMiningCritHit", value: true, "Enables mining critial hit strikes, must hit the required level for it to be active.");
			RequiredLevelForMiningCrit = BindServerConfig("Mining", "RequiredLevelForMiningCrit", 25, "Level the player must be for critical mining hits to activate", advanced: false, 0, 100);
			ChanceForMiningCritHit = BindServerConfig("Mining", "ChanceForMiningCritHit", 0.1f, "Chance for a critical hit when mining", advanced: false, 0f, 1f);
			CriticalHitDmgMult = BindServerConfig("Mining", "CriticalHitDmgMult", 3f, "Multipler for damage from a critical hit", advanced: false, 1f, 20f);
			MiningLootFactor = BindServerConfig("Mining", "MiningLootFactor", 2f, "How much the mining skill provides additional loot. 1 is vanilla (no bonus), 2 is 2x the loot at level 100.", advanced: false, 1f, 10f);
			EnableMiningAOE = BindServerConfig("Mining", "EnableMiningAOE", value: true, "Enable AOE mining skill changes.");
			ChanceForAOEOnHit = BindServerConfig("Mining", "ChanceForAOEOnHit", 0.3f, "Once AOE Mining is enabled, this is the chance for it to activate on any hit", advanced: false, 0f, 1f);
			ChanceForAOEOnHitScalesWithSkill = BindServerConfig("Mining", "ChanceForAOEOnHitScalesWithSkill", value: true, "Increases your chance for an AOE strike based on player skill");
			MiningAOERange = BindServerConfig("Mining", "MiningAOERange", 2f, "How far away the mining AOE is applied. How far away an AOE hit is applied.", advanced: false, 0.5f, 10f);
			MiningAOELevel = BindServerConfig("Mining", "MiningAOELevel", 50f, "The level that AOE mining requires to activate. What skill level Mining AOE is enabled at.", advanced: false, 0f, 100f);
			EnableMiningRockBreaker = BindServerConfig("Mining", "EnableMiningRockBreaker", value: true, "Enable mining whole veins, by a (small) chance.");
			ProtectLeviathansWhenMined = BindServerConfig("Mining", "ProtectLeviathansWhenMined", value: true, "Keeps mined Leviathans (and LeviathanLava) from vanishing out from under you. Mining out the last ore node normally deletes the whole creature instantly, dropping you into the ocean or lava; with this enabled it starts its dive and sinks away instead. Rock breaker and AOE mining also stop rolling the dive reaction once per node.");
			RockBreakerMaxChance = BindServerConfig("Mining", "RockBreakerMaxChance", 0.05f, "The maximum chance to break a whole vein. 0.05 is 5% chance to break a whole vein at level 100. This is checked on each hit.", advanced: false, 0f, 1f);
			RockBreakerRequiredLevel = BindServerConfig("Mining", "RockBreakerRequiredLevel", 75, "The level that vein breaking requires to activate. What skill level whole rocks breaking is enabled at.", advanced: false, 0, 100);
			RockBreakerDamage = BindServerConfig("Mining", "RockBreakerDamage", 300f, "Veinbreakers damage, small damage numbers will mean triggering this will not destroy a whole vein, but massively weaken it. Large numbers will ensure the whole vein is destroyed.", advanced: false, 0f, 10000f);
			MinehitsPerInterval = BindServerConfig("Mining", "MinehitsPerInterval", 2, "The number of pieces per interval to break when mining large rocks.", advanced: true, 1, 100);
			SkillLevelBonusEnabledForMiningDropChance = BindServerConfig("Mining", "SkillLevelBonusEnabledForMiningDropChance", value: false, "Pickaxes skill level provides a bonus to drop chance for drops that are not gaurenteed (This can significantly increase muddy scrap-pile drops).");
			SkipNonRockDropIncreases = BindServerConfig("Mining", "SkipNonRockDropIncreases", value: true, "When enabled, only ores/rocks will get the increased drops, this primarily impacts muddy scrap piles in vanilla.");
			SkipNonRockDropPrefabs = BindServerConfig("Mining", "SkipNonRockDropPrefabs", "LeatherScraps,WitheredBone", "List of prefabs which will not recieve increased mining drops. Should be comma seperated without spaces.");
			ReducedChanceDropsForLowAmountDrops = BindServerConfig("Mining", "ReducedChanceDropsForLowAmountDrops", value: false, "When Enabled, drops that have an amount increase below 1 will only have a chance to happen instead of being rounded up to 1, and always happening.");
			FractionalDropsAsChance = BindServerConfig("Mining", "FractionalDropsAsChance", value: true, "When enabled, drops that are less than 1 become a chance to drop one. When disabled drops below 1 will never result in drops.");
			DistanceMiningDropMultiplierChecks = BindServerConfig("Mining", "DistanceMiningDropMultiplierChecks", 20f, "How far away the loot multiplier will check when rocks are destroyed. Increasing this significantly can cause a performance impact.", advanced: true, 10f, 100f);
			RockbreakerSafetyResetTimeout = BindServerConfig("Mining", "RockbreakerSafetyResetTimeout", 30f, "How long to wait before re-enabling rock breaker after its last activation.", advanced: true, 10f, 120f);
			EnableRun = BindServerConfig("Run", "EnableRun", value: true, "Enable run skill changes.");
			RunSpeedFactor = BindServerConfig("Run", "RunSpeedFactor", 0.005f, "How much the run speed is increased based on your run level. Amount applied per level, 0.005 will make level 100 run give 50% faster running.", advanced: false, 0.001f, 0.06f);
			EnableJump = BindServerConfig("Jump", "EnableJump", value: true, "Enable jump skill changes.");
			JumpHeightFactor = BindServerConfig("Jump", "JumpHeightFactor", 125f, "Percentage of original jump height at skill level 100. 100 = no change, 50 = half height, 200 = double height.", advanced: false, 50f, 300f);
			JumpHeightRequiredLevel = BindServerConfig("Jump", "JumpHeightRequiredLevel", 10f, "Minimum jump skill level required to gain jump height bonuses.", advanced: false, 0f, 100f);
			EnableFallDamageReduction = BindServerConfig("Jump", "EnableFallDamageReduction", value: true, "Enable fall damage reduction based on jump skill.");
			FallDamageReductionFactor = BindServerConfig("Jump", "FallDamageReductionFactor", 0.5f, "How much fall damage is reduced at skill level 100. 0.5 means 50% reduction at max level.", advanced: false, 0f, 1f);
			FallDamageReductionRequiredLevel = BindServerConfig("Jump", "FallDamageReductionRequiredLevel", 15f, "Minimum jump skill level required to gain fall damage reduction.", advanced: false, 0f, 100f);
			EnableFallDamageHeightBonus = BindServerConfig("Jump", "EnableFallDamageHeightBonus", value: true, "Enables Jump skill impacting the distance you can fall without recieving any fall damage.");
			FallDamageHeightRequiredLevel = BindServerConfig("Jump", "FallDamageHeightRequiredLevel", 25f, "The required level to start getting an increase to the height at which you will not recieve any fall damage.", advanced: false, 0f, 100f);
			FallDamageHeightBonus = BindServerConfig("Jump", "FallDamageHeightBonus", 3f, "Bonus to max fall height before taking damage at skill level 100 (meters). Scales with skill level (default is 4, and this is added to that).", advanced: false, 0f, 50f);
			EnableCooking = BindServerConfig("Cooking", "EnableCooking", value: true, "Enable cooking skill changes.");
			CookingBurnReduction = BindServerConfig("Cooking", "CookingBurnReduction", 0.5f, "How much offset is applied to diminishing returns for food, scaled by the players cooking skill. At 1 and cooking 100 food never degrades.", advanced: false, 0.1f, 1f);
			EnableHauling = BindServerConfig("Hauling", "EnableHauling", value: true, "Enables the hauling skill.");
			EnableCarryWeightBonus = BindServerConfig("Hauling", "EnableCarryWeightBonus", value: true, "Enables the carry weight bonus from the hauling skill.");
			HaulingMaxWeightBonus = BindServerConfig("Hauling", "HaulingMaxWeightBonus", 50f, "The maximum carry weight bonus from the hauling skill (the value you get at skill level 100).", advanced: false, 0f, 300f);
			EnableHaulingCartMassReduction = BindServerConfig("Hauling", "EnableHaulingCartMassReduction", value: true, "Enables mass reduction for the cart (this makes the cart easier to move when heavily loaded).");
			HaulingCartMassReduction = BindServerConfig("Hauling", "HaulingCartMassReduction", 0.8f, "The maximum reduction that a carts weight will recieve based on your hauling skill.", advanced: false, 0.01f, 1f);
			HaulingXPRate = BindServerConfig("Hauling", "HaulingXPRate", 0.2f, "The amount of XP that is gained each time with Hauling", advanced: false, 0.01f, 10f);
			HaulingXPCheckInterval = BindServerConfig("Hauling", "HaulingXPCheckInterval", 3, "How many seconds between checks for hauling XP while moving goods, both by cart and by carrying them.", advanced: false, 1, 60);
			HaulingXPCheckInterval.SettingChanged += HaulingXPTracker.OnIntervalChanged;
			HaulingXPMinDistance = BindServerConfig("Hauling", "HaulingXPMinDistance", 1f, "How far you must travel within the check interval to gain hauling XP. Set to 0 to gain hauling XP without moving.", advanced: false, 0f, 50f);
			EnableHaulingCarryWeightXP = BindServerConfig("Hauling", "EnableHaulingCarryWeightXP", value: true, "Enables gaining hauling XP while moving around with a heavily loaded inventory.");
			HaulingCarryWeightXPMinWeight = BindServerConfig("Hauling", "HaulingCarryWeightXPMinWeight", 275f, "The minimum total inventory weight you must be carrying to gain hauling XP from your load.", advanced: false, 0f, 2000f);
			HaulingCarryWeightXPRate = BindServerConfig("Hauling", "HaulingCarryWeightXPRate", 3f, "The amount of XP that is gained each interval while heavily loaded. This is scaled by how loaded you are.", advanced: false, 0.01f, 10f);
			HaulingMaxLoadRatio = BindServerConfig("Hauling", "HaulingMaxLoadRatio", 1.5f, "The maximum load ratio that hauling XP will scale with. Being over your carry weight grants bonus XP, this caps how much.", advanced: true, 1f, 5f);
			EnableBloodMagic = BindServerConfig("BloodMagic", "EnableBloodMagic", value: true, "Enable blood magic skill changes.");
			BloodMagicXPForShieldDamageRatio = BindServerConfig("BloodMagic", "BloodMagicXPForShieldDamageRatio", 50f, "How much XP is gained for shield damage. 50 is once every 50 damage.", advanced: false, 1f, 200f);
			BloodMagicXP = BindServerConfig("BloodMagic", "BloodMagicXP", 1f, "How much XP is gained, used by other blood magic skill settings.", advanced: false, 0.1f, 10f);
			EnableStealth = BindServerConfig("Sneak", "EnableStealth", value: true, "Enable sneak skill changes.");
			SneakSpeedFactor = BindServerConfig("Sneak", "SneakSpeedFactor", 0.03f, "How much sneak speed is increased based on your sneak level. Amount applied per level, 0.03 will make level 100 sneak give normal walkspeed while sneaking.", advanced: false, 0.001f, 0.06f);
			SneakNoiseReductionLevel = BindServerConfig("Sneak", "SneakNoiseReductionLevel", 50f, "The level at which noise reduction starts being applied based on your skill", advanced: false, 0f, 100f);
			SneakNoiseReductionFactor = BindServerConfig("Sneak", "SneakNoiseReductionFactor", 0.5f, "How much noise is reduced based on your sneak level. Amount applied per level, 0.5 will make level 100 sneak give 50% less noise.", advanced: false, 0.1f, 1f);
			EnableSneakBonusDamage = BindServerConfig("Sneak", "EnableSneakBonusDamage", value: true, "Enable sneak bonus damage changes.");
			SneakBackstabBonusLevel = BindServerConfig("Sneak", "SneakBackstabBonusLevel", 25, "The level at which backstab damage starts being applied based on your skill", advanced: false, 0, 100);
			SneakBackstabBonusFactor = BindServerConfig("Sneak", "SneakBackstabBonusFactor", 2f, "How much backstab damage is increased based on your sneak level. 1 is a 100% bonus backstab damage at skill level 100.", advanced: false, 0.1f, 10f);
			EnableAnimalWhisper = BindServerConfig("AnimalHandling", "EnableAnimalWhisper", value: true, "Enable animal handling skill changes.");
			AnimalTamingSpeedFactor = BindServerConfig("AnimalHandling", "AnimalTamingSpeedFactor", 6f, "How much your animal handling skill impacts taming speed. 6 is 6x taming speed at level 100 (5 minutes vs 30 minutes default)", advanced: false, 1f, 10f);
			TamedAnimalLootIncreaseFactor = BindServerConfig("AnimalHandling", "TamedAnimalLootIncreaseFactor", 3f, "How much the animal handling skill improves your loot from tamed creatures. This multiplies the loot the creature would actually have dropped, so it includes its star level, the world resource rate and anything other loot mods change. 1 is vanilla (no bonus), 3 is 3x the loot at level 100", advanced: false, 1f, 10f);
			AnimalHandlingLootRange = BindServerConfig("AnimalHandling", "AnimalHandlingLootRange", 20f, "How close you must be to a tamed creature when it dies to gain the slaughter XP and the bonus loot.", advanced: false, 1f, 100f);
			AnimalHandlingFractionalDropsAsChance = BindServerConfig("AnimalHandling", "AnimalHandlingFractionalDropsAsChance", value: true, "When enabled, a bonus that works out to less than one item becomes a chance at one item. When disabled it is rounded to the nearest whole item, so small drops give nothing at low skill.");
			EnableBeeBonuses = BindServerConfig("AnimalHandling", "EnableBeeBonuses", value: true, "Enables Animal Handling bonuses related to Bees.");
			BetterBeesLevel = BindServerConfig("AnimalHandling", "BetterBeesLevel", 15, "The level at which Bee productivity traits kick in", advanced: false, 0, 100);
			BeeHoneyOutputIncreaseBySkill = BindServerConfig("AnimalHandling", "BeeHoneyOutputIncreaseBySkill", 1f, "At level 100 skill, and 1.0 this results in a 100% increase in honey gathered.");
			EnableBeeBiomeUnrestricted = BindServerConfig("AnimalHandling", "EnableBeeBiomeUnrestricted", value: true, "At the specified level, beeshives built by you can produce honey in any biome.");
			BeeBiomeUnrestrictedLevel = BindServerConfig("AnimalHandling", "BeeBiomeUnrestrictedLevel", 25, "At this level, if enabled, beehives built by you can produce honey in any biome.", advanced: false, 0, 100);
			BeeHarvestXP = BindServerConfig("AnimalHandling", "BeeHarvestXP", 2f, "The amount of xp for Animal handling provided by harvesting a single honey from a beehive", advanced: false, 0f, 20f);
			AnimalBreedingXP = BindServerConfig("AnimalHandling", "AnimalBreedingXP", 5f, "AnimalHandling XP granted to each nearby player when a tamed animal becomes pregnant or gives birth.", advanced: false, 0f, 50f);
			AnimalBreedingXPRange = BindServerConfig("AnimalHandling", "AnimalBreedingXPRange", 30f, "How close a player must be to a breeding animal to gain the breeding XP.", advanced: false, 1f, 100f);
			EnableGathering = BindServerConfig("Farming", "EnableGathering", value: true, "Enable gathering skill changes.");
			EnableGatheringAOE = BindServerConfig("Farming", "EnableGatheringAOE", value: true, "Enable AOE gathering skill changes.");
			GatheringRangeFactor = BindServerConfig("Farming", "GatheringRangeFactor", 5f, "AOE gathering range you have at level 100.", advanced: false, 3f, 25f);
			FarmingRangeRequiredLevel = BindServerConfig("Farming", "GatheringRangeRequiredLevel", 25, "The level that AOE gathering requires to activate.", advanced: false, 0, 100);
			GatheringLuckLevels = BindServerConfig("Farming", "GatheringLuckLevels", "50,70,90", "Higher values have a lower chance of dropping. Each comma seperated number entry (0-100) is a chance at an additional drop.");
			GatheringDisallowedItems = BindServerConfig("Farming", "GatheringDisallowedItems", "SurtlingCore,Flint,Wood,Branch,Stone,Amber,AmberPearl,Coins,Ruby,CryptRemains,Obsidian,Crystal,Pot_Shard,DragonEgg,DvergrLantern,DvergrMineTreasure,SulfurRock,VoltureEgg,Swordpiece,MoltenCore,Hairstrands,Tar,BlackCore", "Items which can be picked, but do not get a luck roll for multiple loot and will not be auto-picked.");
			EnableFarmingBiomeUnrestricted = BindServerConfig("Farming", "EnableFarmingBiomeUnrestricted", value: true, "At the specified level, plants you have tended can grow in any biome.");
			FarmingBiomeUnrestrictedLevel = BindServerConfig("Farming", "FarmingBiomeUnrestrictedLevel", 50, "At this level, if enabled, plants you have tended ignore the biome they were planted in, along with the heat of the Ashlands and the cold of the Mountains and Deep North. They still need cultivated ground, sunlight and room to grow.", advanced: false, 0, 100);
			EnableFarmingMultiPlant = BindServerConfig("Farming", "EnableFarmingMultiPlant", value: true, "Enables farming multi-planting");
			FarmingMultiplantRequiredLevel = BindServerConfig("Farming", "FarmingMultiplantRequiredLevel", 25, "The level that Multiplant is enabled.", advanced: false, 0, 100);
			FarmingMultiplantMaxPlantedAtOnce = BindServerConfig("Farming", "FarmingMultiplantMaxPlantedAtOnce", 12, "The total number of plants that can be planted at once at maximum gathering.");
			FarmingMultiplantCountIncrement = BindServerConfig("Farming", "FarmingMultiplantCountIncrement", 2, "The step size the planting grid grows in as Farming rises. With the default of 2 the grid goes 1 -> 2 -> 4 -> 6 -> 8..., so the count stays even and lays out in tidy rows instead of leaving a ragged one. Set to 1 to grow one plant at a time.", advanced: false, 1, 12);
			FarmingMultiplantColumnCount = BindServerConfig("Farming", "FarmingMultiplantColumnCount", 4, "Maximum number of columns in the planting grid. The grid will form the closest square shape possible without exceeding this limit.", advanced: true, 1, 12);
			FarmingMultiPlantSnapToExisting = BindServerConfig("Farming", "FarmingMultiPlantSnapToExisting", value: true, "Automatically align new grid to nearby existing plants");
			FarmingMultiPlantDistanceBufferModifier = BindClientConfig("Farming", "FarmingMultiPlantDistanceBufferModifier", 1.1f, "The increased distance that is applied to all plants requirements to ensure that they do not become unhealthy. A per-plant minimum derived from its grow radius and collider is always enforced on top, so lowering this cannot crowd a crop to death.");
			FarmingMultiPlantBufferSpace = BindClientConfig("Farming", "FarmingMultiPlantBufferSpace", 0.2f, "Additional space for all multiplanted plants to ensure they are healthy. A per-plant minimum is always enforced on top, so some plants (barley, flax) stay further apart than this setting alone would suggest.", advanced: true, 0f, 5f);
			PlantingCostStaminaReduction = BindServerConfig("Farming", "PlantingCostStaminaReduction", 1f, "At max level, the percentage reduction in stamina cost when planting or cultivating. Applies to every plant in a multiplanted grid as well as single plants. At 1 planting is free at Farming 100 and full price at Farming 0. 0.5 is what the game gives on its own, so that is the floor.", advanced: true, 0.5f, 1f);
			PlantingSnapDistance = BindClientConfig("Farming", "PlantingSnapDistance", 1f, "Extra margin (in meters) added beyond the planting grid's own extent when looking for nearby plants to snap to. The base search area already scales with the grid size and the plant's grow radius.", advanced: true, 0f, 10f);
			PlantingAOEHarvestResetSafety = BindServerConfig("Farming", "PlantingAOEHarvestResetSafety", 10f, "The number of seconds after an AOE harvest that harvesting will be re-enabled, even if it failed to reset.");
			FarmingSnapStyle = BindClientConfig("Farming", "FarmingSnapStyle", "Grid", "'Grid' detects the existing grid pattern and aligns to it; 'Legacy' snaps to the nearest plant position only.", new AcceptableValueList<string>(new string[2] { "Grid", "Legacy" }), advanced: true);
			FarmingSnapPreferCardinal = BindServerConfig("Farming", "FarmingSnapPreferCardinal", value: true, "Legacy snap style only: prefer axis-aligned (N/S/E/W) snap candidates over diagonal ones. Grid style is always lattice-aligned and ignores this.", null, advanced: true);
			EnableSnappingToOtherPlants = BindClientConfig("Farming", "EnableSnappingToOtherPlants", value: true, "When enabled, allows plant grid snapping to plants which are not the same kind as the currently planting one");
			FarmingMultiPlantPersistOrientation = BindServerConfig("Farming", "FarmingMultiPlantPersistOrientation", value: true, "After planting, keep the same grid orientation for the next planting instead of resetting to the direction you're facing. Rotate (scroll) to change it.");
			FarmingMultiPlantRandomRotation = BindClientConfig("Farming", "FarmingMultiPlantRandomRotation", value: true, "Give each multiplanted crop its own random facing, the way vanilla planting does. Disable to have every crop in the grid face the same direction.");
			FarmingMultiPlantCenterRows = BindClientConfig("Farming", "FarmingMultiPlantCenterRows", value: true, "Center the planting grid on your cursor along the row (forward) axis. Disable to grow the grid forward from your cursor instead.");
			FarmingMultiPlantCenterColumns = BindClientConfig("Farming", "FarmingMultiPlantCenterColumns", value: true, "Center the planting grid on your cursor along the column (sideways) axis. Disable to grow the grid sideways from your cursor instead.");
			EnableVoyager = BindServerConfig("Voyager", "EnableVoyager", value: true, "Enable voyager skill changes.");
			VoyagerSkillXPCheckFrequency = BindServerConfig("Voyager", "VoyagerSkillXPCheckFrequency", 5, "How often Voyager skill can be increased while sailing. Rate varies based on your game physics engine speed.", advanced: false, 5, 200);
			VoyagerReduceCuttingStart = BindServerConfig("Voyager", "VoyagerReduceCuttingStart", 50f, "The level where your sails start catching wind closer to head-on, and the sailing penalty for other wind angles starts to shrink. Both improve gradually up to level 100.", advanced: false, 0f, 100f);
			VoyagerCuttingMinAngle = BindServerConfig("Voyager", "VoyagerCuttingMinAngle", 15f, "How close to head-on (in degrees off the bow) the wind can be while your sails still catch all of it, at level 100 Voyager. Vanilla is about 41. The ship HUD's wind ring shades in the angles you have won back.", advanced: false, 0f, 41f);
			VoyagerSailingSpeedFactor = BindServerConfig("Voyager", "VoyagerSailingSpeedFactor", 1.5f, "How much the sailing speed is increased based on your voyager level. Amount applied per level, 2 will make level 100 voyager give 100% faster sailing.", advanced: false, 1f, 20f);
			VoyagerIncreaseExplorationRadius = BindServerConfig("Voyager", "VoyagerIncreaseExplorationRadius", 3f, "How much the exploration radius is increased based on your voyager level. Amount applied per level, 1 will make level 100 voyager give 100% more exploration radius.", advanced: false, 0f, 20f);
			VoyagerPaddleSpeedBonus = BindServerConfig("Voyager", "VoyagerPaddleSpeedBonus", 2f, "How much the paddle speed is increased based on your voyager level. 1 is a 100% bonus at level 100", advanced: false, 0.01f, 5f);
			VoyagerPaddleSpeedBonusLevel = BindServerConfig("Voyager", "VoyagerPaddleSpeedBonusLevel", 25f, "The level that the player starts to get a bonus to paddle speed.", advanced: false, 0f, 100f);
			EnableFriendsRowSpeedBonus = BindServerConfig("Voyager", "EnableFriendsRowSpeedBonus", value: true, "Whether or not friends attached to the ship will provide a 'rowing' speed bonus.");
			MaxFriendsRowSpeedBonus = BindServerConfig("Voyager", "MaxFriendsRowSpeedBonus", 0.25f, "The maximum speed bonus percent that each player can contribute. Players contribution is based on their own Voyager skill level.", advanced: false, 0f, 2f);
			EnableBoatDamageReduction = BindServerConfig("Voyager", "EnableBoatDamageReduction", value: true, "Enable to reduce damage to your boat as Voyager skill level increases.");
			VoyagerDamageReductionAmount = BindServerConfig("Voyager", "VoyagerDamageReductionAmount", 0.9f, "The maximum percentage of damage reduction, at level 100 skill. Scaled to skill. 1 is 100% damage reduction at player skill level 100.", advanced: false, 0f, 1f);
			BoatDamageReductionLevel = BindServerConfig("Voyager", "BoatDamageReductionLevel", 35, "The level at which you start to recieve a damage reduction from damage taken while on the boat");
			VoyagerImpactResistance = BindServerConfig("Voyager", "VoyagerImpactResistance", value: true, "Whether or not impact resistance is enabled.");
			VoyagerImpactResistanceLevel = BindServerConfig("Voyager", "VoyagerImpactResistanceLevel", 75, "The level at which impact resistance is enabled. This prevents the ship you are aboard from taking damage when it bumps into something.");
			EnableWeaponSkill = BindServerConfig("WeaponSkills", "EnableWeaponSkill", value: true, "Enable weapon skill changes.");
			WeaponSkillStaminaReduction = BindServerConfig("WeaponSkills", "WeaponSkillStaminaReduction", 0.5f, "How much stamina is reduced based on your weapon skill level at level 100. 0.5 will make level 100 weapon skill give 50% less stamina cost.", advanced: false, 0f, 1f);
			WeaponSkillParryBonus = BindServerConfig("WeaponSkills", "WeaponSkillParryBonus", 1f, "How much extra XP you get for parrying an attack", advanced: false, 0f, 10f);
			WeaponSkillBowDrawStaminaCostReduction = BindServerConfig("WeaponSkills", "WeaponSkillBowDrawStaminaCostReduction", 0.5f, "How much stamina is reduced based on your weapon skill level at level 100. 0.5 will make level 100 weapon skill give 50% less stamina cost. Vanilla is .33", advanced: false, 0f, 1f);
			WeaponSkillEquipRequiredLevel = BindServerConfig("WeaponSkills", "WeaponSkillEquipRequiredLevel", 45, "The level that equip speed bonuses start being applied based on your skill", advanced: true, 0, 100);
			WeaponSkillEquipSpeedFactor = BindServerConfig("WeaponSkills", "WeaponSkillEquipSpeedFactor", 2f, "How much faster your equipment is equipped based on your weapon skill.", advanced: true, 0f, 20f);
			EnableBlocking = BindServerConfig("Blocking", "EnableBlocking", value: true, "Enable blocking skill changes.");
			BlockPowerRequiredLevel = BindServerConfig("Blocking", "BlockPowerRequiredLevel", 25, "The level that BlockPowerFactor start being applied based on your skill", advanced: true, 0, 100);
			BlockPowerFactor = BindServerConfig("Blocking", "BlockPowerFactor", 0.75f, "The factor that blocking power increases after reaching the BlockRequiredLevel. Vanilla is .5f", advanced: true, 0.5f, 2f);
			BlockStaminaGainRequiredLevel = BindServerConfig("Blocking", "BlockStaminaGainRequiredLevel", 40, "The level that BlockStaminaGainFactor starts being applied base on your skill", advanced: true, 0, 100);
			BlockStaminaGainFactor = BindServerConfig("Blocking", "BlockStaminaGainFactor", 0.5f, "How much block stamina is returned based on your blocking level. This is modified by your characters blocking level. At level 100 you gain twice your block stamina use's stamina", advanced: true, 0.5f, 2f);
			EnableParryStaminaGain = BindServerConfig("Blocking", "EnableParryStaminaGain", value: false, "Enable stamina gain on parry. Value based off BlockStaminaGainFactor");
			EnableCrafting = BindServerConfig("Crafting", "EnableCrafting", value: true, "Enable crafting skill changes.");
			EnableDurabilityLossPrevention = BindServerConfig("Crafting", "EnableDurabilityLossPrevention", value: true, "Enables durability reduction prevention that can scale with player skill.");
			EnableDurabilitySaves = BindServerConfig("Crafting", "EnableDurabilitySaves", value: true, "Enables reducing how often you use durability for your items.");
			DurabilitySaveLevel = BindServerConfig("Crafting", "DurabilitySaveLevel", 25, "Level requirement to enable durability saves.");
			ChanceForDurabilityLossPrevention = BindServerConfig("Crafting", "ChanceForDurabilityLossPrevention", 0.25f, "Chance that you will not use durability on use.", advanced: false, 0f, 1f);
			ScaleDurabilitySaveBySkillLevel = BindServerConfig("Crafting", "ScaleDurabilitySaveBySkillLevel", value: true, "Reduces MaxChanceForDurabilityPreserveOnUse based on level, at lvl 50 crafting MaxChanceForDurabilityPreserveOnUse is 50% of its value.");
			EnableBonusItemCrafting = BindServerConfig("Crafting", "EnableBonusItemCrafting", value: true, "Enables crafting bonus items.");
			CraftingBonusCraftsLevel = BindServerConfig("Crafting", "CraftingBonusCraftsLevel", 50, "The level at which you can start getting bonus crafts.", advanced: false, 0, 100);
			CraftingMaxBonus = BindServerConfig("Crafting", "CraftingMaxBonus", 3, "The maximum number of additional bonus crafts you can get from crafting an item");
			CraftingBonusChance = BindServerConfig("Crafting", "CraftingBonusChance", 0.3f, "The chance to get a bonus craft when crafting an item. 0.5 is a 50% chance to get a bonus craft at level 100. Bonus crafting success can stack up to the CraftingMaxBonus times.", advanced: false, 0f, 1f);
			EnableCraftBonusAsFraction = BindServerConfig("Crafting", "EnableCraftBonusAsFraction", value: true, "Enable crafting bonus as a fraction of the number crafted (for recipes where the result is more than 1). If disabled, the bonus is always 1 item.");
			CraftBonusFractionOfCraftNumber = BindServerConfig("Crafting", "CraftBonusFractionOfCraftNumber", 0.25f, "If the number of items crafted by the recipe is greater than 1, a percentage of the number crafted is used for the bonus. This determines that percentage. Eg: craft 20 arrows (1 craft) a .25 value would give you 5 arrows for 1 bonus craft.");
			EnableMaterialReturns = BindServerConfig("Crafting", "EnableMaterialReturns", value: true, "Enable material returns from crafting.");
			CraftingMaterialReturnsLevel = BindServerConfig("Crafting", "CraftingMaterialReturnsLevel", 75, "The level at which material returns start being applied based on your skill", advanced: false, 0, 100);
			MaxCraftingMaterialReturnPercent = BindServerConfig("Crafting", "MaxCraftingMaterialReturnPercent", 0.3f, "The maximum percentage of materials that can be returned from crafting. 0.5 is 50% at level 100.", advanced: false, 0f, 1f);
			ChanceForMaterialReturn = BindServerConfig("Crafting", "ChanceForMaterialReturn", 0.15f, "The chance to return materials when crafting an item. 0.25 is a 25% chance to return materials at level 100.", advanced: false, 0f, 1f);
			ScaleCraftedEquipmentQuality = BindServerConfig("Crafting", "ScaleCraftedEquipmentQuality", value: true, "Recipes that consume an item carrying a quality level and produce equipment now craft that equipment at a higher quality instead of producing more of it. The quality granted is the average quality of the ingredients spent, rounded down, and is still capped by what your crafting station could normally build. The fishing hat made from twelve good fish comes out upgraded, and infusing an Ashlands weapon carries its star level over instead of resetting it to one. Crafting always spends the lowest quality ingredient that covers the recipe, so this never eats your best one when a worse one would do.");
			EnableCooking = BindServerConfig("Cooking", "EnableCooking", value: true, "Enable cooking skill changes.");
			EnableCookingDegradeReduction = BindServerConfig("Cooking", "EnableCookingDegradeReduction", value: true, "When enabled, cooking contributes to preventing food from degrading over time.");
			EnableCookingBonusItems = BindServerConfig("Cooking", "EnableCookingBonusItems", value: true, "Enables or disables getting bonuse food items.");
			RequiredLevelForBonusCookingItems = BindServerConfig("Cooking", "RequiredLevelForBonusCookingItems", 10, "Minimum level to start crafting bonus food items.", advanced: false, 0, 100);
			ChanceForCookingBonusItems = BindServerConfig("Cooking", "ChanceForCookingBonusItems", 0.4f, "The chance to craft bonus food items at level 100. 0.4 is a 40% chance.", advanced: false, 0f, 1f);
			CookingBonusItemMaxAmount = BindServerConfig("Cooking", "CookingBonusItemMaxAmount", 3, "The maximum number of cooked items you can recieve as a bonus, each item is an additional chance roll", advanced: true, 0, 10);
			CookingBurnReduction = BindServerConfig("Cooking", "CookingBurnReduction", 0.5f, "How much offset is applied to diminishing returns for food, scaled by the players cooking skill. At 1 and cooking 100 food never degrades.", advanced: false, 0.1f, 1f);
			EnableCookingStreak = BindServerConfig("Cooking", "EnableCookingStreak", value: true, "Enables the cooking streak. Cooking several foods in a row grants bonus cooking XP that ramps up with each food cooked. Crafting at the cauldron and taking cooked food off a cooking station or oven both count.");
			CookingStreakBonusPerFood = BindServerConfig("Cooking", "CookingStreakBonusPerFood", 0.1f, "How much bonus cooking XP each consecutive food adds to the next one cooked. 0.1 is 10% more XP per food in the streak.", advanced: false, 0f, 1f);
			CookingStreakMaxBonus = BindServerConfig("Cooking", "CookingStreakMaxBonus", 1f, "The largest bonus a cooking streak can reach. 1 is double XP.", advanced: false, 0f, 10f);
			CookingStreakTimeout = BindServerConfig("Cooking", "CookingStreakTimeout", 120f, "How many seconds you can go without cooking before the streak ends. Crafting other things or placing raw food on a station does not end it. 0 means the streak never ends.", advanced: false, 0f, 600f);
			CookingStreakShowText = BindClientConfig("Cooking", "CookingStreakShowText", value: true, "Shows a popup with the current cooking streak bonus as it climbs. This is a per player setting.");
			EnableCookingEatXP = BindServerConfig("Cooking", "EnableCookingEatXP", value: true, "Grants cooking XP when you eat food, scaled by the total health, stamina and eitr the food provides.");
			CookingEatXPPerFoodStat = BindServerConfig("Cooking", "CookingEatXPPerFoodStat", 0.01f, "Cooking XP gained per point of food stats (health + stamina + eitr) when eating. At 0.01 a 40 health 30 stamina food gives 0.7 XP, about the same as taking one item off a cooking station.", advanced: false, 0f, 1f);
			EnableKnowledgeSharing = BindServerConfig("SkillRates", "EnableKnowledgeSharing", value: true, "Enable shared knowledge, this allows you to gain faster experiance in low skills if you already have other high skills (eg switching primary weapon skill).");
			AnimalTamingSkillGainRate = BindServerConfig("SkillRates", "AnimalTamingSkillGainRate", 1f, "How fast the skill is gained.", advanced: false, 0f, 50f);
			VoyagerSkillGainRate = BindServerConfig("SkillRates", "VoyagerSkillGainRate", 4f, "How fast the skill is gained.", advanced: false, 0f, 50f);
			SharedKnowledgeSkillBonusRate = BindServerConfig("SkillRates", "SharedKnowledgeSkillBonusRate", 1.5f, "How strong at maximum the xp bonus from shared knowledge will be when catching up skills lower than your highest.", advanced: false, 0f, 10f);
			SharedKnowledgeCap = BindServerConfig("SkillRates", "SharedKnowledgeCap", 5f, "The number of levels below your maximum skill that shared knowledge stops providing a bonus at. Eg: max skill 90, at 5 any skills 85+ will not recieve an xp bonus.", advanced: true, 0f, 50f);
			SharedKnowledgeIgnoreList = BindServerConfig("SkillRates", "SharedKnowledgeIgnoreList", "", "Comma separated list of skills to ignore when calculating shared knowledge. This is useful for skills that have vastly different XP curves or that you simply do not want an accelerated growth rate in. Invalid skill names will be ignored.");
			SharedKnowledgeIgnoreList.SettingChanged += SharedKnowledge.UnallowedSharedXPSkillTypesChanged;
			AdditionalSkillNames = BindServerConfig("SkillRates", "AdditionalSkillNames", "", "Comma separated list of extra skill names to create gain rate entries for. Skills added by other mods are detected automatically, so this is only needed when one is missed. Use the skill name (SkillManager) or the skill identifier (Jotunn) exactly as the other mod defines it. New entries appear after the next game start.");
			AdditionalSkillNames.SettingChanged += SkillRates.AdditionalSkillNamesChanged;
			EnableSwimming = BindServerConfig("Swimming", "EnableSwimming", value: true, "Enable swimming skill changes.");
			EnableSwimStaminaCostReduction = BindServerConfig("Swimming", "EnableSwimStaminaCostReduction", value: true, "Enables swim stamina cost reduction, at the level specified by SwimStaminaReductionLevel.");
			SwimSpeedRequiredLevel = BindServerConfig("Swimming", "SwimSpeedRequiredLevel", 25, "The level that swimming speed increases start being applied based on your skill", advanced: false, 0, 100);
			SwimmingSpeedFactor = BindServerConfig("Swimming", "SwimmingSpeedFactor", 3f, "How much swimming speed is increased based on your swimming level. This is modified by your characters swimming level. At skill level 100 the full value is in effect.", advanced: false, 0.1f, 10f);
			SwimStaminaReductionLevel = BindServerConfig("Swimming", "SwimStaminaReductionLevel", 50, "The level that swim stamina cost reductions start being applied based on your skill", advanced: false, 0, 100);
			SwimStaminaCostReductionFactor = BindServerConfig("Swimming", "SwimStaminaCostReductionFactor", 0.5f, "How much swim stamina cost is reduced based on your swimming level. This is modified by your characters swimming level. At skill level 100 the full value is in effect.", advanced: false, 0.1f, 1f);
			EnableQualityIngredientScaling = BindServerConfig("Fishing", "EnableQualityIngredientScaling", value: true, "Recipes that take a whole fish now produce more when a higher quality fish is used, and always spend the lowest quality fish that can cover the recipe. Vanilla only pays out for fish quality when turning a fish into raw fish, and will otherwise happily consume your best catch for the same result a quality 1 fish gives.");
			QualityIngredientOutputMultiplier = BindServerConfig("Fishing", "QualityIngredientOutputMultiplier", 1f, "How strongly fish quality increases the crafted amount. 1 is one extra result per quality level above 1, so a quality 5 anglerfish makes 5 fish wraps instead of 1. Multiplies the recipes own quality result multiplier, so recipes tuned by other mods keep their own scaling.", advanced: false, 0f, 10f);
			RestrictQualityScalingToFish = BindServerConfig("Fishing", "RestrictQualityScalingToFish", value: true, "Only let fish earn extra output. This gates the bonus amount only - always spending the lowest quality ingredient that covers a recipe, and carrying ingredient quality into crafted equipment (ScaleCraftedEquipmentQuality), apply to everything regardless. Turning this off matters for the Ashlands weapons, which also carry a quality level, and for mods that give quality levels to other materials - in which case those recipes would start producing more as well.");
			ScaleNonStackingCraftOutputs = BindServerConfig("Fishing", "ScaleNonStackingCraftOutputs", value: true, "Also scale recipes whose result does not stack, such as the fish based mead bases. Vanilla skips its own bonus craft rolls for these. Crafting more than will fit is safe - the craft is refused with an inventory full message rather than losing items.");
			PreferOtherPlantGrid = BindServerConfig("Mod Compatibility", "PreferOtherPlantGrid", value: true, "When enabled, and an other planting grid mod is enabled (), Impactful skills planting grid will be disabled.");
		}

		internal static void SetupMainFileWatcher()
		{
			FileSystemWatcher fileSystemWatcher = new FileSystemWatcher();
			fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
			fileSystemWatcher.Path = Path.GetDirectoryName(cfg.ConfigFilePath);
			fileSystemWatcher.Filter = "MidnightsFX.ImpactfulSkills.cfg";
			fileSystemWatcher.Changed += OnConfigFileChanged;
			fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher.EnableRaisingEvents = true;
		}

		internal static void BeginBatchBind()
		{
			cfg.SaveOnConfigSet = false;
		}

		internal static void EndBatchBind(bool save)
		{
			cfg.SaveOnConfigSet = true;
			if (save)
			{
				cfg.Save();
			}
		}

		private static void OnConfigFileChanged(object sender, FileSystemEventArgs e)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				Logger.LogInfo("Configuration file has been changed, reloading settings.");
				cfg.Reload();
			}
		}

		internal static void SendXPForSkillInArea(DataObjects.XPIncreaseRequest xp_increase)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			Logger.LogDebug($"Requesting {xp_increase.Skill} xp increase {xp_increase.Amount}");
			ZPackage val = xp_increase.ToPackage();
			if (ZNet.instance.IsServer())
			{
				SendXPRequestToInRangePlayers(xp_increase, val);
				GrantLocalPlayerXP(xp_increase);
				return;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null)
			{
				SkillIncreaseXP.SendPackage(serverPeer.m_uid, val);
			}
		}

		private static void GrantLocalPlayerXP(DataObjects.XPIncreaseRequest xpdetails)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && Vector3.Distance(((Component)Player.m_localPlayer).transform.position, (Vector3)xpdetails.Location) <= xpdetails.Range)
			{
				((Character)Player.m_localPlayer).RaiseSkill(xpdetails.Skill, xpdetails.Amount);
			}
		}

		internal static IEnumerator OnServerReceiveXPRequest(long sender, ZPackage pkg)
		{
			if (pkg != null)
			{
				DataObjects.XPIncreaseRequest xPIncreaseRequest = DataObjects.XPIncreaseRequest.FromPackage(pkg);
				SendXPRequestToInRangePlayers(xPIncreaseRequest, xPIncreaseRequest.ToPackage());
			}
			yield break;
		}

		private static void SendXPRequestToInRangePlayers(DataObjects.XPIncreaseRequest xpdetails, ZPackage pkg)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			foreach (ZNetPeer item in Util.ServerGetPeersInArea(xpdetails.Location, xpdetails.Range))
			{
				SkillIncreaseXP.SendPackage(item.m_uid, pkg);
			}
		}

		internal static IEnumerator OnClientReceiveXPGrant(long sender, ZPackage pkg)
		{
			if (pkg != null && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				DataObjects.XPIncreaseRequest xPIncreaseRequest = DataObjects.XPIncreaseRequest.FromPackage(pkg);
				((Character)Player.m_localPlayer).RaiseSkill(xPIncreaseRequest.Skill, xPIncreaseRequest.Amount);
			}
			yield break;
		}

		public static ConfigEntry<KeyboardShortcut> BindClientConfig(string category, string key, KeyboardShortcut value, string description, bool advanced = false)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			return cfg.Bind<KeyboardShortcut>(category, key, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = false,
				IsAdvanced = advanced
			} }));
		}

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

		public static ConfigEntry<float> BindClientConfig(string catagory, string key, float value, string description, bool advanced = false, float valmin = 0f, float valmax = 150f)
		{
			//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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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 = false,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<string> BindClientConfig(string catagory, string key, string value, string description, AcceptableValueList<string> acceptableValues = null, 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			return cfg.Bind<string>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = false,
				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_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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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
			} }));
		}

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

		public static ConfigEntry<int> BindServerConfig(string catagory, string key, int value, string description, bool advanced = false, int valmin = 0, int valmax = 150)
		{
			//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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			return cfg.Bind<int>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(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_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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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
			} }));
		}

		public static ConfigEntry<string> BindServerConfig(string catagory, string key, string value, string description, AcceptableValueList<string> acceptableValues = null, 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			return cfg.Bind<string>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}
	}
	internal static class IngredientQuality
	{
		internal enum CraftMode
		{
			None,
			Amount,
			ItemQuality
		}

		[HarmonyPatch(typeof(Player), "ConsumeResources")]
		internal static class ConsumeQualityIngredientTierPatch
		{
			[HarmonyPrefix]
			private static bool Prefix(Player __instance, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier)
			{
				if (!AnyFeatureEnabled || itemQuality >= 0 || requirements == null)
				{
					return true;
				}
				CraftingStation currentCraftingStation = __instance.GetCurrentCraftingStation();
				bool flag = false;
				Requirement[] array = requirements;
				foreach (Requirement requirement in array)
				{
					if (IsSpentAt(requirement, currentCraftingStation) && HasQuality(requirement))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					return true;
				}
				Inventory inventory = ((Humanoid)__instance).GetInventory();
				array = requirements;
				foreach (Requirement val in array)
				{
					if (!IsSpentAt(val, currentCraftingStation))
					{
						continue;
					}
					int num = val.GetAmount(qualityLevel) * multiplier;
					if (num <= 0)
					{
						continue;
					}
					int num2 = -1;
					if (HasQuality(val))
					{
						int num3 = SelectTier(inventory, val.m_resItem.m_itemData, num);
						if (num3 > 0)
						{
							num2 = num3;
						}
					}
					string name = val.m_resItem.m_itemData.m_shared.m_name;
					Logger.LogDebug($"Consuming {num} {name} from quality tier {num2}.");
					inventory.RemoveItem(name, num, num2, true);
				}
				return false;
			}
		}

		private static int[] TierCounts = new int[6];

		internal static bool AnyFeatureEnabled
		{
			get
			{
				if (!ValConfig.EnableQualityIngredientScaling.Value)
				{
					return ValConfig.ScaleCraftedEquipmentQuality.Value;
				}
				return true;
			}
		}

		internal static bool HasQuality(Requirement requirement)
		{
			if (requirement == null || (Object)(object)requirement.m_resItem == (Object)null)
			{
				return false;
			}
			return requirement.m_resItem.m_itemData.m_shared.m_maxQuality > 1;
		}

		internal static bool CarriesQuality(Requirement requirement)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			if (!HasQuality(requirement))
			{
				return false;
			}
			SharedData shared = requirement.m_resItem.m_itemData.m_shared;
			if (ValConfig.RestrictQualityScalingToFish.Value)
			{
				return (int)shared.m_itemType == 21;
			}
			return true;
		}

		internal static bool IsSpentAt(Requirement requirement, CraftingStation station)
		{
			if (requirement == null || (Object)(object)requirement.m_resItem == (Object)null)
			{
				return false;
			}
			if (!((Object)(object)station != (Object)null))
			{
				return !requirement.m_upgraderResource;
			}
			return station.m_upgrader == requirement.m_upgraderResource;
		}

		internal static CraftMode Classify(Recipe recipe, CraftingStation station, out int amountIndex)
		{
			amountIndex = -1;
			if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
			{
				return CraftMode.None;
			}
			if (recipe.m_requireOnlyOneIngredient)
			{
				return CraftMode.None;
			}
			Requirement[] resources = recipe.m_resources;
			if (resources == null)
			{
				return CraftMode.None;
			}
			int num = 0;
			int num2 = 0;
			int num3 = -1;
			for (int i = 0; i < resources.Length; i++)
			{
				if (IsSpentAt(resources[i], station) && HasQuality(resources[i]))
				{
					num++;
					if (CarriesQuality(resources[i]))
					{
						num2++;
						num3 = i;
					}
				}
			}
			if (num == 0)
			{
				return CraftMode.None;
			}
			if (recipe.m_item.m_itemData.m_shared.m_maxQuality > 1)
			{
				if (!ValConfig.ScaleCraftedEquipmentQuality.Value)
				{
					return CraftMode.None;
				}
				return CraftMode.ItemQuality;
			}
			if (!ValConfig.EnableQualityIngredientScaling.Value)
			{
				return CraftMode.None;
			}
			if (num > 1 || num2 != 1)
			{
				return CraftMode.None;
			}
			if (recipe.m_item.m_itemData.m_shared.m_maxStackSize <= 1 && !ValConfig.ScaleNonStackingCraftOutputs.Value)
			{
				return CraftMode.None;
			}
			amountIndex = num3;
			return CraftMode.Amount;
		}

		internal static int PanelCraftMultiplier()
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || ((RecipeDataPair)(ref instance.m_selectedRecipe)).ItemData != null)
			{
				return 1;
			}
			if (!ZInput.GetButton("AltPlace") && !ZInput.GetButton("JoyLStick"))
			{
				return 1;
			}
			return instance.m_multiCraftAmount;
		}

		internal static int SelectTier(Inventory inventory, ItemData prototype, int required)
		{
			if (inventory == null || prototype == null || required <= 0)
			{
				return 0;
			}
			int maxQuality = prototype.m_shared.m_maxQuality;
			if (maxQuality <= 1)
			{
				return 0;
			}
			if (TierCounts.Length < maxQuality + 1)
			{
				TierCounts = new int[maxQuality + 1];
			}
			Array.Clear(TierCounts, 0, maxQuality + 1);
			string name = prototype.m_shared.m_name;
			foreach (ItemData item in inventory.m_inventory)
			{
				if (item != null && !(item.m_shared.m_name != name) && item.m_worldLevel >= Game.m_worldLevel && item.m_quality >= 1 && item.m_quality <= maxQuality)
				{
					TierCounts[item.m_quality] += item.m_stack;
				}
			}
			for (int i = 1; i <= maxQuality; i++)
			{
				if (TierCounts[i] >= required)
				{
					return i;
				}
			}
			return 0;
		}
	}
	internal static class LocalizationLoader
	{
		internal const string LocalizationFolder = "Localizations";

		private static readonly Regex CommentLine = new Regex("^\\s*//.*$", RegexOptions.Multiline);

		internal static void AddLocalizations()
		{
			CustomLocalization localization = ImpactfulSkills.Localization;
			string text = Path.Combine(Paths.ConfigPath, "ImpactfulSkills", "Localizations");
			Directory.CreateDirectory(text);
			Logger.LogInfo("Loading Localizations.");
			string[] manifestResourceNames = typeof(ImpactfulSkills).Assembly.GetManifestResourceNames();
			foreach (string text2 in manifestResourceNames)
			{
				if (!text2.Contains("Localizations"))
				{
					continue;
				}
				string text3 = LanguageNameFromResource(text2);
				if (text3 == null)
				{
					Logger.LogWarning("Could not determine a language name for '" + text2 + "', skipping it.");
					continue;
				}
				try
				{
					AddLocalization(localization, text2, text3, Path.Combine(text, text3 + ".json"));
				}
				catch (Exception ex)
				{
					Logger.LogError("Failed to load localization '" + text3 + "': " + ex.Message);
				}
			}
		}

		private static void AddLocalization(CustomLocalization Localization, string embeddedResource, string language, string translationFile)
		{
			Dictionary<string, string> dictionary = SimpleJson.DeserializeObject<Dictionary<string, string>>(CommentLine.Replace(ReadEmbeddedResourceFile(embeddedResource), ""));
			List<string> token_order = dictionary.Keys.ToList();
			Dictionary<string, string> dictionary2 = dictionary;
			bool flag = true;
			if (File.Exists(translationFile))
			{
				try
				{
					dictionary2 = SimpleJson.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(translationFile));
					flag = UpdateLocalizationWithMissingKeys(dictionary, dictionary2);
				}
				catch
				{
					Logger.LogWarning("'" + translationFile + "' could not be parsed, resetting it to the built in " + language + " localization.");
					dictionary2 = dictionary;
					flag = true;
				}
			}
			string text = SerializePretty(dictionary2, token_order);
			if (flag)
			{
				File.WriteAllText(translationFile, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				Logger.LogDebug("Wrote " + translationFile);
			}
			Localization.AddJsonFile(language, text);
			Logger.LogDebug("Added localization: '" + language + "'");
		}

		private static bool UpdateLocalizationWithMissingKeys(Dictionary<string, string> internal_localization, Dictionary<string, string> cached_localization)
		{
			bool result = false;
			List<string> list = cached_localization.Keys.Where((string key) => !internal_localization.ContainsKey(key)).ToList();
			if (list.Count > 0)
			{
				Logger.LogDebug("Removing extra keys " + string.Join(",", list) + ".");
				foreach (string item in list)
				{
					cached_localization.Remove(item);
				}
				result = true;
			}
			foreach (KeyValuePair<string, string> item2 in internal_localization)
			{
				if (!cached_localization.ContainsKey(item2.Key))
				{
					Logger.LogDebug("Adding missing localization key " + item2.Key);
					cached_localization.Add(item2.Key, item2.Value);
					result = true;
				}
			}
			return result;
		}

		private static string SerializePretty(Dictionary<string, string> localization, List<string> token_order)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{\n");
			for (int i = 0; i < token_order.Count; i++)
			{
				string text = token_order[i];
				stringBuilder.Append("  \"").Append(EscapeJsonString(text)).Append("\": \"")
					.Append(EscapeJsonString(localization[text]))
					.Append('"');
				if (i < token_order.Count - 1)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append('\n');
			}
			stringBuilder.Append("}\n");
			return stringBuilder.ToString();
		}

		private static string EscapeJsonString(string value)
		{
			StringBuilder stringBuilder = new StringBuilder(value.Length);
			foreach (char c in value)
			{
				switch (c)
				{
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '\b':
					stringBuilder.Append("\\b");
					continue;
				case '\f':
					stringBuilder.Append("\\f");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\r':
					stringBuilder.Append("\\r");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}

		private static string LanguageNameFromResource(string embeddedResource)
		{
			string[] array = embeddedResource.Split(new char[1] { '.' });
			int num = Array.IndexOf(array, "Localizations");
			if (num < 0 || num + 2 > array.Length - 1)
			{
				return null;
			}
			return array[num + 1];
		}

		private static string ReadEmbeddedResourceFile(string filename)
		{
			using Stream stream = typeof(ImpactfulSkills).Assembly.GetManifestResourceStream(filename);
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}
	}
	internal class Logger
	{
		public static LogLevel Level = (LogLevel)16;

		public static void enableDebugLogging(object sender, EventArgs e)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (ValConfig.EnableDebugMode.Value)
			{
				Level = (LogLevel)32;
			}
			else
			{
				Level = (LogLevel)16;
			}
		}

		public static void setDebugLogging(bool state)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (state)
			{
				Level = (LogLevel)32;
			}
			else
			{
				Level = (LogLevel)16;
			}
		}

		public static void LogDebug(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)Level >= 32)
			{
				ImpactfulSkills.Log.LogInfo((object)message);
			}
		}

		public static void LogInfo(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)Level >= 16)
			{
				ImpactfulSkills.Log.LogInfo((object)message);
			}
		}

		public static void LogWarning(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)Level >= 4)
			{
				ImpactfulSkills.Log.LogWarning((object)message);
			}
		}

		public static void LogError(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)Level >= 2)
			{
				ImpactfulSkills.Log.LogError((object)message);
			}
		}
	}
	[BepInPlugin("MidnightsFX.ImpactfulSkills", "ImpactfulSkills", "0.18.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[SynchronizationMode(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	internal class ImpactfulSkills : BaseUnityPlugin
	{
		public const string PluginGUID = "MidnightsFX.ImpactfulSkills";

		public const string PluginName = "ImpactfulSkills";

		public const string PluginVersion = "0.18.0";

		public ValConfig cfg;

		public static CustomLocalization Localization = LocalizationManager.Instance.GetLocalization();

		internal static AssetBundle EmbeddedResourceBundle;

		public static ManualLogSource Log;

		public void Awake()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			cfg = new ValConfig(((BaseUnityPlugin)this).Config);
			EmbeddedResourceBundle = AssetUtils.LoadAssetBundleFromResources("ImpactfulSkills.AssetsEmbedded.impactfulskills", typeof(ImpactfulSkills).Assembly);
			LocalizationLoader.AddLocalizations();
			Gathering.SetupGatherables();
			Mining.SetupMining();
			AnimalWhisper.SetupAnimalSkill();
			Voyaging.SetupSailingSkill();
			Hauling.SetupHaulingSkill();
			HaulingXPTracker.Create();
			SkillRates.SetupSkillRateConfigs();
			((MonoBehaviour)this).StartCoroutine(SkillRates.DiscoverModdedSkillsDeferred());
			Modcheck.CheckModCompat();
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			new Harmony("MidnightsFX.ImpactfulSkills").PatchAll(executingAssembly);
		}
	}
}
namespace ImpactfulSkills.modules
{
	internal static class SkillRates
	{
		public class SkillRateConfig
		{
			public string SkillName { get; set; }

			public string DisplayName { get; set; }

			public ConfigEntry<float> ConfigEntry { get; set; }
		}

		[HarmonyPatch(typeof(Skills), "Awake")]
		public static class PatchSkillsAwakeFindSkills
		{
			[HarmonyPostfix]
			[HarmonyPriority(0)]
			private static void Postfix(Skills __instance)
			{
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				if (__instance.m_skills == null)
				{
					return;
				}
				int num = 0;
				ValConfig.BeginBatchBind();
				foreach (SkillDef skill in __instance.m_skills)
				{
					if (skill != null && EnsureSkillRateConfig(skill.m_skill))
					{
						num++;
					}
				}
				ValConfig.EndBatchBind(num > 0);
				if (num > 0)
				{
					Logger.LogInfo($"Added skill gain rate settings for {num} skill(s) that registered after startup.");
				}
			}
		}

		[HarmonyPatch(typeof(Skills), "RaiseSkill")]
		public static class PatchSkillIncreaseHigherGainsForLowerSkills
		{
			[HarmonyPrefix]
			[HarmonyPriority(500)]
			private static void Prefix(SkillType skillType, ref float factor)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				SkillType val = NormalizeSkillType(skillType);
				if (!SkillRateConfigs.TryGetValue(val, out var value))
				{
					if (SkipSkills.Contains(val))
					{
						return;
					}
					EnsureSkillRateConfigLate(val);
					if (!SkillRateConfigs.TryGetValue(val, out value))
					{
						return;
					}
				}
				factor *= value.ConfigEntry.Value;
			}
		}

		private const string ConfigSection = "SkillRates";

		private const string ConfigKeySuffix = "SkillGainRate";

		private const string SkillManagerTypeName = "SkillManager.Skill";

		private const string SkillManagerRegistryField = "skillByName";

		private static Dictionary<SkillType, SkillRateConfig> SkillRateConfigs = new Dictionary<SkillType, SkillRateConfig>();

		private static Dictionary<SkillType, string> DiscoveredSkillNames = new Dictionary<SkillType, string>();

		private static Dictionary<string, SkillType> UsedConfigKeys = new Dictionary<string, SkillType>(StringComparer.OrdinalIgnoreCase);

		private static HashSet<SkillType> SkipSkills = new HashSet<SkillType>();

		internal static void SetupSkillRateConfigs()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			ValConfig.BeginBatchBind();
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if (EnsureSkillRateConfig(value))
				{
					num++;
				}
			}
			ValConfig.EndBatchBind(num > 0);
		}

		internal static IEnumerator DiscoverModdedSkillsDeferred()
		{
			yield return null;
			DiscoverModdedSkills();
		}

		internal static void DiscoverModdedSkills()
		{
			CollectJotunnSkills();
			CollectSkillManagerSkills();
			CollectAdditionalSkillNames();
			BindDiscoveredSkills(save: true);
		}

		public static void AdditionalSkillNamesChanged(object sender, EventArgs e)
		{
			try
			{
				CollectAdditionalSkillNames();
				BindDiscoveredSkills(save: false);
			}
			catch (Exception ex)
			{
				Logger.LogWarning("Error handling the updated AdditionalSkillNames setting: " + ex.Message);
			}
		}

		private static void BindDiscoveredSkills(bool save)
		{
			int num = 0;
			ValConfig.BeginBatchBind();
			SkillType[] array = DiscoveredSkillNames.Keys.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				if (EnsureSkillRateConfig(array[i]))
				{
					num++;
				}
			}
			ValConfig.EndBatchBind(save && num > 0);
			if (num > 0)
			{
				Logger.LogInfo($"Added skill gain rate settings for {num} skill(s) added by other mods.");
			}
		}

		private static void CollectJotunnSkills()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(SkillManager), "CustomSkills");
				if (fieldInfo == null || !(fieldInfo.GetValue(SkillManager.Instance) is IDictionary dictionary))
				{
					Logger.LogWarning("Could not read Jotunns custom skill registry. Skills added through Jotunn will only get a gain rate setting once they first grant xp.");
					return;
				}
				foreach (DictionaryEntry item in dictionary)
				{
					SkillType skill = (SkillType)item.Key;
					object? value = item.Value;
					object? obj = ((value is SkillConfig) ? value : null);
					RecordDiscoveredSkill(skill, (obj != null) ? ((SkillConfig)obj).Identifier : null, "Jotunn");
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning("Error reading Jotunns custom skill registry: " + ex.Message);
			}
		}

		private static void CollectSkillManagerSkills()
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					Type type = assembly.GetType("SkillManager.Skill", throwOnError: false);
					if (type == null)
					{
						continue;
					}
					FieldInfo field = type.GetField("skillByName", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (field == null || !(field.GetValue(null) is IDictionary dictionary))
					{
						Logger.LogWarning(assembly.GetName().Name + " uses SkillManager, but its skill registry could not be read. Its skills will only get a gain rate setting once they first grant xp.");
						continue;
					}
					foreach (DictionaryEntry item in dictionary)
					{
						string text = item.Key as string;
						if (!string.IsNullOrEmpty(text))
						{
							RecordDiscoveredSkill(NameToSkillType(text), text, "SkillManager in " + assembly.GetName().Name);
						}
					}
				}
				catch (Exception ex)
				{
					Logger.LogDebug("Skipped " + assembly.GetName().Name + " while looking for SkillManager skills: " + ex.Message);
				}
			}
		}

		private static void CollectAdditionalSkillNames()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(ValConfig.AdditionalSkillNames.Value))
			{
				return;
			}
			string[] array = ValConfig.AdditionalSkillNames.Value.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0)
				{
					SkillType skill;
					SkillType val = (TryResolveSkill(text, out skill) ? skill : NameToSkillType(text));
					if (!Skills.IsSkillValid(val))
					{
						Logger.LogWarning("AdditionalSkillNames entry '" + text + "' does not match any registered skill, ignoring it. It needs to be the skill name or identifier the mod that adds the skill uses.");
					}
					else
					{
						RecordDiscoveredSkill(val, text, "AdditionalSkillNames");
					}
				}
			}
		}

		private static void RecordDiscoveredSkill(SkillType skill, string skill_name, string source)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected I4, but got Unknown
			skill = NormalizeSkillType(skill);
			if ((int)skill != 0 && !string.IsNullOrEmpty(skill_name) && !DiscoveredSkillNames.ContainsKey(skill))
			{
				DiscoveredSkillNames[skill] = skill_name;
				Logger.LogDebug($"Found mod added skill '{skill_name}' ({(int)skill}) via {source}.");
			}
		}

		private static bool EnsureSkillRateConfig(SkillType skill)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected I4, but got Unknown
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			skill = NormalizeSkillType(skill);
			if (SkillRateConfigs.ContainsKey(skill) || SkipSkills.Contains(skill))
			{
				return false;
			}
			if ((int)skill == 0 || (int)skill == 999 || skill == Voyaging.VoyagingSkill || skill == Hauling.HaulingSkill || skill == AnimalWhisper.AnimalHandling)
			{
				SkipSkills.Add(skill);
				return false;
			}
			string text = ResolveConfigName(skill);
			string text2 = LocalizedSkillName(skill) ?? text;
			try
			{
				ConfigEntry<float> configEntry = ValConfig.BindServerConfig("SkillRates", UniqueConfigKey(skill, text), 1f, "How fast the " + text2 + " skill is gained. 1 is the default rate, below 1 slows it down and 0 stops it being gained at all.", advanced: false, 0f, 50f);
				SkillRateConfigs[skill] = new SkillRateConfig
				{
					SkillName = text,
					DisplayName = text2,
					ConfigEntry = configEntry
				};
				return true;
			}
			catch (Exception ex)
			{
				Logger.LogWarning($"Could not create a skill gain rate setting for '{text}' ({(int)skill}): {ex.Message}");
				SkipSkills.Add(skill);
				return false;
			}
		}

		private unsafe static string ResolveConfigName(SkillType skill)
		{
			//IL_0005: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected I4, but got Unknown
			if (DiscoveredSkillNames.TryGetValue(skill, out var value))
			{
				return Sanitize(value);
			}
			if (Enum.IsDefined(typeof(SkillType), skill))
			{
				return ((object)(*(SkillType*)(&skill))/*cast due to .constrained prefix*/).ToString();
			}
			string text = LocalizedSkillName(skill);
			if (text != null)
			{
				return Sanitize(text);
			}
			return ((int)skill).ToString();
		}

		private static string UniqueConfigKey(SkillType skill, string skill_name)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected I4, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected I4, but got Unknown
			string text = skill_name + "SkillGainRate";
			if (UsedConfigKeys.TryGetValue(text, out var value) && value != skill)
			{
				text = string.Format("{0}_{1}{2}", skill_name, (int)skill, "SkillGainRate");
				Logger.LogInfo($"More than one skill is named '{skill_name}', using '{text}' for the one with id {(int)skill}.");
			}
			UsedConfigKeys[text] = skill;
			return text;
		}

		private unsafe static string LocalizedSkillName(SkillType skill)
		{
			if (Localization.instance == null)
			{
				return null;
			}
			string text = Localization.instance.Localize("$skill_" + ((object)(*(SkillType*)(&skill))/*cast due to .constrained prefix*/).ToString().ToLower());
			if (string.IsNullOrEmpty(text) || text.StartsWith("["))
			{
				return null;
			}
			return text;
		}

		private static string Sanitize(string skill_name)
		{
			StringBuilder stringBuilder = new StringBuilder(skill_name.Length);
			foreach (char c in skill_name)
			{
				if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '.' || c == '-')
				{
					stringBuilder.Append(c);
				}
			}
			if (stringBuilder.Length <= 0)
			{
				return Math.Abs(StringExtensionMethods.GetStableHashCode(skill_name)).ToString();
			}
			return stringBuilder.ToString();
		}

		internal static SkillType NameToSkillType(string skill_name)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return NormalizeSkillType((SkillType)StringExtensionMethods.GetStableHashCode(skill_name));
		}

		internal static bool TryResolveSkill(string skill_name, out SkillType skill)
		{
			//IL_0032: 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_0038: 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_0041: Invalid comparison between Unknown and I4
			//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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected I4, but got Unknown
			//IL_009a: 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_005c: Expected I4, but got Unknown
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected I4, but got Unknown
			skill = (SkillType)0;
			if (string.IsNullOrEmpty(skill_name))
			{
				return false;
			}
			skill_name = skill_name.Trim();
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0 && (int)value != 999 && ((object)value/*cast due to .constrained prefix*/).ToString().Equals(skill_name, StringComparison.OrdinalIgnoreCase))
				{
					skill = (SkillType)(int)value;
					return true;
				}
			}
			SkillType val2 = NameToSkillType(skill_name);
			if (DiscoveredSkillNames.ContainsKey(val2) || SkillRateConfigs.ContainsKey(val2))
			{
				skill = (SkillType)(int)val2;
				return true;
			}
			foreach (KeyValuePair<SkillType, string> discoveredSkillName in DiscoveredSkillNames)
			{
				string text = LocalizedSkillName(discoveredSkillName.Key);
				if (text != null && text.Equals(skill_name, StringComparison.OrdinalIgnoreCase))
				{
					skill = (SkillType)(int)discoveredSkillName.Key;
					return true;
				}
			}
			return false;
		}

		private static SkillType NormalizeSkillType(SkillType skill)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Expected I4, but got Unknown
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			int num = (int)skill;
			if (num >= 0 || num == int.MinValue)
			{
				return skill;
			}
			return (SkillType)Math.Abs(num);
		}

		private static void EnsureSkillRateConfigLate(SkillType skill)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			ValConfig.BeginBatchBind();
			bool num = EnsureSkillRateConfig(skill);
			ValConfig.EndBatchBind(num);
			if (num)
			{
				Logger.LogInfo("Created a skill gain rate setting for '" + SkillRateConfigs[NormalizeSkillType(skill)].DisplayName + "' the first time it granted xp. Restart for it to be synchronized to clients.");
			}
		}
	}
}
namespace ImpactfulSkills.modules.Multiplant
{
	internal class Plantable
	{
		public float GrowRadius { get; set; }

		public float Extent { get; set; }

		public GameObject Refgo { get; set; }

		public List<Requirement> Seeds { get; set; }
	}
	internal static class PlantDefinitions
	{
		[HarmonyPriority(100)]
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		public static class Patch_ZNetScene_Awake
		{
			private static void Postfix()
			{
				BuildPlantRequirements();
			}
		}

		internal static Dictionary<string, Plantable> PlantableDefinitions = new Dictionary<string, Plantable>();

		internal static readonly int plantSpaceMask = LayerMask.GetMask(new string[5] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid" });

		internal static readonly int GhostLayer = LayerMask.NameToLayer("ghost");

		internal const float SpacingSafetyMargin = 0.05f;

		internal static float MaxGrowRadius { get; private set; }

		internal static void BuildPlantRequirements()
		{
			PlantableDefinitions.Clear();
			MaxGrowRadius = 0f;
			if ((Object)(object)ZNetScene.instance == (Object)null || ZNetScene.instance.m_prefabs == null)
			{
				Logger.LogWarning("ZNetScene not ready for plant definitions");
				return;
			}
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				Plant component = prefab.GetComponent<Plant>();
				if ((Object)(object)component == (Object)null || PlantableDefinitions.ContainsKey(((Object)prefab).name))
				{
					continue;
				}
				List<Requirement> list = new List<Requirement>();
				Piece component2 = prefab.GetComponent<Piece>();
				if ((Object)(object)component2 != (Object)null)
				{
					Requirement[] resources = component2.m_resources;
					foreach (Requirement item in resources)
					{
						list.Add(item);
					}
				}
				float num = HorizontalExtent(prefab);
				PlantableDefinitions.Add(((Object)prefab).name, new Plantable
				{
					GrowRadius = component.m_growRadius,
					Extent = num,
					Refgo = prefab,
					Seeds = list
				});
				if (component.m_growRadius > MaxGrowRadius)
				{
					MaxGrowRadius = component.m_growRadius;
				}
				GameObject[] grownPrefabs = component.m_grownPrefabs;
				foreach (GameObject val in grownPrefabs)
				{
					if (!PlantableDefinitions.ContainsKey(((Object)val).name))
					{
						PlantableDefinitions.Add(((Object)val).name, new Plantable
						{
							Refgo = val,
							GrowRadius = component.m_growRadius,
							Extent = HorizontalExtent(val)
						});
					}
				}
				Logger.LogDebug($"Added plant cache entry: {((Object)prefab).name} growRadius={component.m_growRadius:F2} " + $"extent={num:F2} required={RequiredDistance(component.m_growRadius, num):F2} " + $"spacing={SpacingFor(component.m_growRadius, num):F2}");
			}
			Logger.LogInfo($"Loaded {PlantableDefinitions.Count} plantable definitions");
		}

		internal static float HorizontalExtent(GameObject prefab)
		{
			//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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)prefab == (Object)null)
			{
				return 0f;
			}
			float num = 0f;
			Vector3 position = pref