Decompiled source of Oathbound v0.22.1

BepInEx/plugins/WarriorRpg/Warrior.Core.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Warrior.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+36024a3abb676c88560741336bac1ca89fd65235")]
[assembly: AssemblyProduct("Warrior.Core")]
[assembly: AssemblyTitle("Warrior.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Warrior.Core
{
	public static class MartialTrees
	{
		private static readonly TreeWeb.Node[] BerserkerNodes = new TreeWeb.Node[25]
		{
			new TreeWeb.Node("b_grip", "Twin Grip", "Paired-axe attacks cost 3% less stamina. Berserker combat talents require paired axes in hand.", "Flint", Bonus.AttackCost, 0.03f),
			new TreeWeb.Node("b_edge", "Keen Edges", "Paired axes deal 3% more damage.", "Bronze", Bonus.Damage, 0.03f),
			new TreeWeb.Node("b_breath", "Fighting Breath", "While wielding paired axes, normal stamina regeneration is 4% faster.", "MeadStaminaMinor", Bonus.Regen, 0.04f),
			new TreeWeb.Node("b_hone", "Honed Edges", "Paired axes deal another 4% more damage.", "Iron", Bonus.Damage, 0.04f),
			new TreeWeb.Node("b_frenzy", "Frenzy", "Every third paired-axe hit on the same enemy critically hits for 35% extra damage. Land each hit within 8 seconds; changing targets resets the chain.", "TrophyBoar"),
			new TreeWeb.Node("b_control", "Controlled Grip", "Paired-axe attacks cost another 3% less stamina.", "LeatherScraps", Bonus.AttackCost, 0.03f),
			new TreeWeb.Node("b_weight", "Weighted Blades", "Paired-axe hits cause 6% more stagger.", "Copper", Bonus.FistStagger, 0.06f),
			new TreeWeb.Node("b_pace", "Measured Pace", "Paired-axe attacks cost another 4% less stamina.", "Bronze", Bonus.AttackCost, 0.04f),
			new TreeWeb.Node("b_rend", "Rend", "A paired-axe hit wounds its enemy for 6 seconds: 20% of the hit, capped at 12 bleed damage. Once every 8 seconds; wounds do not stack.", "Bloodbag"),
			new TreeWeb.Node("b_brace", "Braced Stance", "While wielding paired axes, take 5% less stagger buildup.", "HelmetLeather", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("b_drive", "Driving Blades", "Paired-axe hits cause another 5% more stagger.", "Iron", Bonus.FistStagger, 0.05f),
			new TreeWeb.Node("b_rooted", "Rooted Stance", "While wielding paired axes, take another 5% less stagger buildup.", "ArmorLeatherLegs", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("b_impact", "Axe Rush", "Starting a dodge primes your next paired-axe hit within 5 seconds with strong knockback. Once every 8 seconds; large enemies resist through normal mass.", "HelmetTrollLeather"),
			new TreeWeb.Node("b_step", "Sidestep", "While wielding paired axes, dodging costs 5% less stamina.", "DeerHide", Bonus.Dodge, 0.05f),
			new TreeWeb.Node("b_stride", "Long Stride", "While wielding paired axes, running costs 5% less stamina.", "CapeDeerHide", Bonus.Run, 0.05f),
			new TreeWeb.Node("b_ready", "Ready Footwork", "While wielding paired axes, dodging costs another 5% less stamina.", "ArmorLeatherLegs", Bonus.Dodge, 0.05f),
			new TreeWeb.Node("b_counter", "Countercut", "A dodge primes your next paired-axe hit within 5 seconds to restore 8 stamina. Once every 10 seconds. Missing does not consume it.", "MeadStaminaMedium"),
			new TreeWeb.Node("b_recover", "Battle Hardened", "Gain 5 maximum health while this class is active. Adds to food bonuses; does not refill health.", "MeadHealthMinor", Bonus.MaximumHealth, 5f),
			new TreeWeb.Node("b_lungs", "Deep Breath", "Gain 5 maximum stamina while this class is active. Adds to food bonuses; does not refill stamina.", "MeadStaminaMinor", Bonus.MaximumStamina, 5f),
			new TreeWeb.Node("b_resolve", "Firm Resolve", "While wielding paired axes, take another 5% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("b_blood", "Bloodsurge", "A paired-axe hit while below half health restores 3 health. Once every 12 seconds. Cannot revive you.", "MeadHealthMedium"),
			new TreeWeb.Node("b_roam", "Relentless Stride", "While wielding paired axes, running costs another 5% less stamina.", "CapeWolf", Bonus.Run, 0.05f),
			new TreeWeb.Node("b_spring", "Spring Step", "While wielding paired axes, jumping costs 10% less stamina.", "CapeFeather", Bonus.Jump, 0.1f),
			new TreeWeb.Node("b_force", "Heavy Hands", "Paired-axe hits cause another 5% more stagger.", "Iron", Bonus.FistStagger, 0.05f),
			new TreeWeb.Node("b_whirl", "Whirlwind", "Spin with both axes. Up to five enemies within 3.5 metres take 30 slash with heavy stagger and knockback. Requires paired axes. 35 stamina, every 15 seconds.", "TrophySkeleton")
		};

		private static readonly TreeWeb.Lane[] BerserkerLanes = new TreeWeb.Lane[12]
		{
			new TreeWeb.Lane("b_whirl", "Storm of Axes", new TreeWeb.Node("b_whirl_wide", "Wider Arc", "Whirlwind reaches another metre.", "AxeIron"), new TreeWeb.Node("b_whirl_edge", "Storm Edges", "Whirlwind deals 30% more damage.", "Iron"), new TreeWeb.Node("b_whirl_swift", "Quick Recovery", "Whirlwind's cooldown falls from 15 to 11 seconds.", "LeatherScraps"), new TreeWeb.Node("b_whirl_blood", "Red Harvest", "Every enemy Whirlwind strikes is wounded for 6 seconds: 20% of the hit, capped at 12 bleed damage. Wounds refresh instead of stacking.", "Bloodbag")),
			new TreeWeb.Lane("b_whirl", "Reaver", new TreeWeb.Node("b_reave_grip", "Reaver's Grip", "Paired-axe attacks cost another 2% less stamina.", "LeatherScraps", Bonus.AttackCost, 0.02f), new TreeWeb.Node("b_reave_edge", "Reaver's Edge", "Paired axes deal another 3% more damage.", "Bronze", Bonus.Damage, 0.03f), new TreeWeb.Node("b_reave_force", "Reaver's Weight", "Paired-axe hits cause another 5% more stagger.", "Copper", Bonus.FistStagger, 0.05f), new TreeWeb.Node("b_cleave", "Cleave", "Every third same-target paired-axe hit also strikes one visible enemy within 3m for 30% of the hit, capped at 10 damage. Eight-second cooldown; cannot chain.", "TrophySkeleton")),
			new TreeWeb.Lane("b_frenzy", "Bloodlust", new TreeWeb.Node("b_fren_deep", "Rising Fury", "Frenzy's critical hit deals 55% extra damage instead of 35%.", "Iron"), new TreeWeb.Node("b_fren_hold", "Long Rage", "Frenzy's chain holds for 12 seconds between hits instead of 8.", "TrophyBoar"), new TreeWeb.Node("b_fren_edge", "Bloodied Edges", "Paired axes deal another 2% more damage.", "Bronze", Bonus.Damage, 0.02f), new TreeWeb.Node("b_fren_carry", "Unbroken Frenzy", "Your chain no longer resets when you change enemies. Only the chain window can break it.", "AxeIron")),
			new TreeWeb.Lane("b_frenzy", "Berserkergang", new TreeWeb.Node("b_gang_grip", "Wrapped Grips", "Paired-axe attacks cost another 2% less stamina.", "LeatherScraps", Bonus.AttackCost, 0.02f), new TreeWeb.Node("b_gang_hide", "Scarred Hide", "While wielding paired axes, gain 3 armour.", "ArmorLeatherChest", Bonus.Armour, 3f), new TreeWeb.Node("b_gang_wind", "Wolf's Breath", "While wielding paired axes, normal stamina regeneration is another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("b_gang_rage", "Berserkergang", "While below half health your paired-axe attacks deal 20% more damage and cause double stagger.", "TrophyWolf")),
			new TreeWeb.Lane("b_rend", "Butcher", new TreeWeb.Node("b_rend_deep", "Deep Rend", "Rend's wound is capped at 18 instead of 12.", "Bloodbag"), new TreeWeb.Node("b_rend_quick", "Quick Rend", "Rend's cooldown falls from 8 to 6 seconds.", "Flint"), new TreeWeb.Node("b_rend_edge", "Serrated Heads", "Paired axes deal another 2% more damage.", "Iron", Bonus.Damage, 0.02f), new TreeWeb.Node("b_rend_long", "Slow Wound", "Rend's wound runs four seconds longer at the same rate: five ticks instead of three.", "Bloodbag"), new TreeWeb.Node("b_rend_scent", "Blood in the Water", "Your paired-axe hits on an enemy already bleeding from Rend deal 15% more damage.", "WolfFang")),
			new TreeWeb.Lane("b_rend", "Gore", new TreeWeb.Node("b_gore_edge", "Gutting Edge", "Paired axes deal another 2% more damage.", "Bronze", Bonus.Damage, 0.02f), new TreeWeb.Node("b_gore_weight", "Cleaving Weight", "Paired-axe hits cause another 5% more stagger.", "Copper", Bonus.FistStagger, 0.05f), new TreeWeb.Node("b_gore_heart", "Hot Blood", "While wielding paired axes, normal health regeneration is 5% faster.", "MeadHealthMinor", Bonus.Health, 0.05f), new TreeWeb.Node("b_gore_feast", "Feast", "Killing an enemy that is bleeding from Rend restores 12 health and readies Rend at once. Once every 15 seconds.", "RawMeat")),
			new TreeWeb.Lane("b_impact", "Avalanche", new TreeWeb.Node("b_rush_force", "Full Weight", "Axe Rush's knockback is half again as strong.", "Stone"), new TreeWeb.Node("b_rush_quick", "Short Wind-up", "Axe Rush's cooldown falls from 8 to 6 seconds.", "ArmorLeatherLegs"), new TreeWeb.Node("b_rush_drive", "Driving Shoulder", "Paired-axe hits cause another 5% more stagger.", "Iron", Bonus.FistStagger, 0.05f), new TreeWeb.Node("b_rush_wave", "Avalanche", "Axe Rush also throws back every other enemy within 3 metres of the one you hit. Large enemies resist through their mass.", "TrophyGreydwarf")),
			new TreeWeb.Lane("b_impact", "Ironbark", new TreeWeb.Node("b_bark_hide", "Boiled Hide", "While wielding paired axes, gain 2 armour.", "ArmorLeatherChest", Bonus.Armour, 2f), new TreeWeb.Node("b_bark_jaw", "Set Jaw", "While wielding paired axes, take another 3% less stagger buildup.", "HelmetLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("b_bark_lungs", "Deep Lungs", "While wielding paired axes, normal stamina regeneration is another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("b_bark_scar", "Scar Tissue", "While wielding paired axes, gain 1 armour for every tenth of your maximum health that is missing, up to 6.", "WolfPelt")),
			new TreeWeb.Lane("b_counter", "Counterblow", new TreeWeb.Node("b_cnt_deep", "Deep Draught", "Countercut restores 16 stamina instead of 8.", "MeadStaminaMedium"), new TreeWeb.Node("b_cnt_quick", "Quick Turn", "Countercut's cooldown falls from 10 to 7 seconds.", "Flint"), new TreeWeb.Node("b_cnt_roll", "Turning Shoulders", "While wielding paired axes, dodging costs another 3% less stamina.", "ArmorLeatherLegs", Bonus.Dodge, 0.03f), new TreeWeb.Node("b_cnt_answer", "Answer in Kind", "The Countercut hit also deals 30% more damage and cannot be blocked.", "AxeIron")),
			new TreeWeb.Lane("b_counter", "Skirmisher", new TreeWeb.Node("b_skir_step", "Loose Feet", "While wielding paired axes, dodging costs another 3% less stamina.", "DeerHide", Bonus.Dodge, 0.03f), new TreeWeb.Node("b_skir_stride", "Loping Stride", "While wielding paired axes, running costs another 3% less stamina.", "CapeDeerHide", Bonus.Run, 0.03f), new TreeWeb.Node("b_skir_leap", "Spring Heels", "While wielding paired axes, jumping costs another 5% less stamina.", "CapeFeather", Bonus.Jump, 0.05f), new TreeWeb.Node("b_skir_wind", "Fighting Wind", "Dodging while below half health restores 10 stamina. Once every 10 seconds.", "MeadStaminaMedium")),
			new TreeWeb.Lane("b_blood", "Lifeblood", new TreeWeb.Node("b_life_deep", "Deeper Draught", "Bloodsurge restores 8 health instead of 3.", "MeadHealthMedium"), new TreeWeb.Node("b_life_quick", "Quick Clotting", "Bloodsurge's cooldown falls from 12 to 8 seconds.", "Bloodbag"), new TreeWeb.Node("b_life_tide", "Rising Tide", "Bloodsurge triggers below 70% health instead of 50%.", "MeadHealthMinor"), new TreeWeb.Node("b_life_heart", "Strong Heart", "While wielding paired axes, normal health regeneration is another 5% faster.", "MeadHealthMinor", Bonus.Health, 0.05f), new TreeWeb.Node("b_life_pay", "Blood for Blood", "While below a quarter health, Bloodsurge heals twice as much and your paired-axe hits cannot be blocked.", "TrophyWolf")),
			new TreeWeb.Lane("b_blood", "Thick Blood", new TreeWeb.Node("b_thick_back", "Broad Back", "While wielding paired axes, gain 2 armour.", "ArmorLeatherChest", Bonus.Armour, 2f), new TreeWeb.Node("b_thick_gut", "Iron Gut", "While wielding paired axes, take another 3% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("b_thick_wind", "Bellows", "While wielding paired axes, normal stamina regeneration is another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("b_thick_death", "Death Refuses", "Survive an enemy hit below 30% health to restore 10 health, and for 4 seconds your paired-axe attacks cost no stamina. Every 45 seconds.", "MeadHealthMedium"))
		};

		private static readonly TreeWeb.Node[] BerserkerAll = TreeWeb.Flatten(BerserkerNodes, BerserkerLanes);

		private static readonly TreeWeb.Node[] BreakerNodes = new TreeWeb.Node[25]
		{
			new TreeWeb.Node("bk_grip", "Two-Handed Grip", "Two-handed axe attacks cost 3% less stamina. Breaker combat talents require a two-handed axe in hand.", "Wood", Bonus.AttackCost, 0.03f),
			new TreeWeb.Node("bk_edge", "Keen Edge", "Two-handed axes deal 3% more damage.", "Bronze", Bonus.Damage, 0.03f),
			new TreeWeb.Node("bk_breath", "Fighting Breath", "While wielding a two-handed axe, normal stamina regeneration is 4% faster.", "MeadStaminaMinor", Bonus.Regen, 0.04f),
			new TreeWeb.Node("bk_hone", "Honed Edge", "Two-handed axes deal another 4% more damage.", "Iron", Bonus.Damage, 0.04f),
			new TreeWeb.Node("bk_fracture", "Fracture", "Each two-handed axe hit leaves a Fracture on that enemy, up to 4. Each adds 4% stagger buildup against it. They mend after 8 seconds.", "Flint"),
			new TreeWeb.Node("bk_control", "Controlled Grip", "Two-handed axe attacks cost another 3% less stamina.", "LeatherScraps", Bonus.AttackCost, 0.03f),
			new TreeWeb.Node("bk_weight", "Weighted Head", "Two-handed axe hits cause 6% more stagger.", "Copper", Bonus.FistStagger, 0.06f),
			new TreeWeb.Node("bk_pace", "Measured Pace", "Two-handed axe attacks cost another 4% less stamina.", "Bronze", Bonus.AttackCost, 0.04f),
			new TreeWeb.Node("bk_shatter", "Shatter", "A two-handed axe hit on a staggered enemy spends every Fracture on it for 10% more damage each, then clears them.", "Battleaxe"),
			new TreeWeb.Node("bk_brace", "Braced Stance", "While wielding a two-handed axe, take 5% less stagger buildup.", "HelmetLeather", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("bk_drive", "Driving Head", "Two-handed axe hits cause another 5% more stagger.", "Iron", Bonus.FistStagger, 0.05f),
			new TreeWeb.Node("bk_rooted", "Rooted Stance", "While wielding a two-handed axe, take another 5% less stagger buildup.", "ArmorLeatherLegs", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("bk_quake", "Quake", "A two-handed axe hit on a staggered enemy leaves a Fracture on every other enemy within 4 metres.", "Stone"),
			new TreeWeb.Node("bk_step", "Sidestep", "While wielding a two-handed axe, dodging costs 5% less stamina.", "DeerHide", Bonus.Dodge, 0.05f),
			new TreeWeb.Node("bk_stride", "Long Stride", "While wielding a two-handed axe, running costs 5% less stamina.", "CapeDeerHide", Bonus.Run, 0.05f),
			new TreeWeb.Node("bk_ready", "Ready Footwork", "While wielding a two-handed axe, dodging costs another 5% less stamina.", "ArmorLeatherLegs", Bonus.Dodge, 0.05f),
			new TreeWeb.Node("bk_anvil", "Anvil", "While wielding a two-handed axe, take 10% less stagger buildup and 5% less damage from everything.", "HelmetIron"),
			new TreeWeb.Node("bk_recover", "Battle Hardened", "Gain 5 maximum health while this class is active. Adds to food bonuses; does not refill health.", "MeadHealthMinor", Bonus.MaximumHealth, 5f),
			new TreeWeb.Node("bk_lungs", "Deep Breath", "Gain 5 maximum stamina while this class is active. Adds to food bonuses; does not refill stamina.", "MeadStaminaMinor", Bonus.MaximumStamina, 5f),
			new TreeWeb.Node("bk_roam", "Relentless Stride", "While wielding a two-handed axe, running costs another 5% less stamina.", "CapeWolf", Bonus.Run, 0.05f),
			new TreeWeb.Node("bk_endure", "Long Work", "A two-handed axe kill restores 4 health and 6 stamina. Once every 8 seconds.", "MeadHealthMedium"),
			new TreeWeb.Node("bk_force", "Heavy Hands", "Two-handed axe hits cause another 5% more stagger.", "Iron", Bonus.FistStagger, 0.05f),
			new TreeWeb.Node("bk_march", "Breaker's March", "Armour weighs on you 40% less: that share of the movement penalty from your equipped armour is ignored.", "ArmorIronLegs", Bonus.Burden, 0.4f),
			new TreeWeb.Node("bk_resolve", "Firm Resolve", "While wielding a two-handed axe, take another 5% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("bk_sunder", "Sunder", "Drive the axe into the ground. Up to 6 enemies within 8 metres take 22 blunt, are staggered, thrown back and left with 3 Fractures. 35 stamina, every 45s.", "Stone")
		};

		private static readonly TreeWeb.Lane[] BreakerLanes = new TreeWeb.Lane[12]
		{
			new TreeWeb.Lane("bk_fracture", "Deep Cracks", new TreeWeb.Node("bk_frc_deep", "Deeper Cracks", "Each Fracture adds 6% stagger buildup instead of 4%.", "Flint"), new TreeWeb.Node("bk_frc_high", "Spiderweb", "An enemy holds 6 Fractures instead of 4.", "Crystal"), new TreeWeb.Node("bk_frc_hold", "Slow to Mend", "Fractures last 14 seconds instead of 8.", "Coal"), new TreeWeb.Node("bk_frc_wide", "Split Grain", "A two-handed axe hit also leaves a Fracture on one other enemy within 3 metres.", "AxeIron"), new TreeWeb.Node("bk_frc_first", "Hairline", "Your first two-handed axe hit on an unfractured enemy leaves 2 Fractures instead of 1.", "Stone")),
			new TreeWeb.Lane("bk_fracture", "Whetstone", new TreeWeb.Node("bk_wht_edge", "Whetted Edge", "Two-handed axes deal another 3% more damage.", "Bronze", Bonus.Damage, 0.03f), new TreeWeb.Node("bk_wht_grip", "Wrapped Grip", "Two-handed axe attacks cost another 2% less stamina.", "LeatherScraps", Bonus.AttackCost, 0.02f), new TreeWeb.Node("bk_wht_wind", "Whetstone Breath", "While wielding a two-handed axe, normal stamina regeneration is another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("bk_wht_bite", "Bite", "A two-handed axe hit on an enemy at full health deals 20% more damage. Once every 6 seconds.", "BattleaxeCrystal")),
			new TreeWeb.Lane("bk_shatter", "Ruin", new TreeWeb.Node("bk_run_deep", "Deeper Ruin", "Shatter pays 15% per Fracture instead of 10%.", "Iron"), new TreeWeb.Node("bk_run_keep", "Lingering Ruin", "A Shatter leaves 2 Fractures behind instead of clearing them all.", "Coal"), new TreeWeb.Node("bk_run_deny", "Keep Them Down", "A staggered enemy you hit with a two-handed axe cannot regenerate health for 6 seconds.", "Bloodbag"), new TreeWeb.Node("bk_run_take", "Spoils of Ruin", "A Shatter restores 5 health and 8 stamina. Once every 8 seconds.", "MeadHealthMedium")),
			new TreeWeb.Lane("bk_shatter", "Cleaver", new TreeWeb.Node("bk_clv_edge", "Cleaver's Edge", "Two-handed axes deal another 3% more damage.", "Bronze", Bonus.Damage, 0.03f), new TreeWeb.Node("bk_clv_weight", "Cleaver's Weight", "Two-handed axe hits cause another 5% more stagger.", "Copper", Bonus.FistStagger, 0.05f), new TreeWeb.Node("bk_clv_grip", "Cleaver's Grip", "Two-handed axe attacks cost another 2% less stamina.", "LeatherScraps", Bonus.AttackCost, 0.02f), new TreeWeb.Node("bk_clv_wide", "Wide Swing", "A two-handed axe hit on a staggered enemy also strikes one other enemy within 3 metres for 30% of the hit, capped at 12. Every 8 seconds.", "AxeIron")),
			new TreeWeb.Lane("bk_quake", "Aftershock", new TreeWeb.Node("bk_aft_push", "Full Weight", "Two-handed axe hits on a staggered enemy throw it back hard.", "Stone"), new TreeWeb.Node("bk_aft_wave", "Ground Gives", "A two-handed axe hit that staggers an enemy throws back everything within 3 metres. Every 10 seconds; large enemies resist.", "MaceIron"), new TreeWeb.Node("bk_aft_drive", "Driving Shoulder", "Two-handed axe hits cause another 5% more stagger.", "Iron", Bonus.FistStagger, 0.05f), new TreeWeb.Node("bk_aft_reach", "Wider Quake", "Quake reaches 6 metres instead of 4.", "CapeLox"), new TreeWeb.Node("bk_aft_crack", "Crack the Ground", "An enemy you throw back with a two-handed axe is left with 2 Fractures.", "Obsidian")),
			new TreeWeb.Lane("bk_quake", "Footing", new TreeWeb.Node("bk_ftg_step", "Loose Feet", "While wielding a two-handed axe, dodging costs another 3% less stamina.", "DeerHide", Bonus.Dodge, 0.03f), new TreeWeb.Node("bk_ftg_stride", "Loping Stride", "While wielding a two-handed axe, running costs another 3% less stamina.", "CapeDeerHide", Bonus.Run, 0.03f), new TreeWeb.Node("bk_ftg_grip", "Sure Hands", "Two-handed axe attacks cost another 2% less stamina.", "LeatherScraps", Bonus.AttackCost, 0.02f), new TreeWeb.Node("bk_ftg_wind", "Second Wind", "While three or more enemies stand within 8 metres, your two-handed axe attacks cost 15% less stamina.", "MeadStaminaMedium")),
			new TreeWeb.Lane("bk_anvil", "Immovable", new TreeWeb.Node("bk_imv_hide", "Scarred Hide", "While wielding a two-handed axe, gain 3 armour.", "ArmorLeatherChest", Bonus.Armour, 3f), new TreeWeb.Node("bk_imv_gut", "Braced Gut", "While wielding a two-handed axe, take another 3% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("bk_imv_deep", "Deeper Anvil", "Anvil's damage reduction rises from 5% to 9%.", "HelmetIron"), new TreeWeb.Node("bk_imv_scar", "Scar Tissue", "While wielding a two-handed axe, gain 1 armour for every tenth of your maximum health that is missing, up to 6.", "WolfPelt")),
			new TreeWeb.Lane("bk_anvil", "Ironshod", new TreeWeb.Node("bk_irn_shod", "Ironshod", "Armour weighs on you another 40% less. With Breaker's March, four-fifths of the penalty is gone.", "ArmorIronChest", Bonus.Burden, 0.4f), new TreeWeb.Node("bk_irn_back", "Broad Back", "While wielding a two-handed axe, gain 2 armour.", "ArmorLeatherChest", Bonus.Armour, 2f), new TreeWeb.Node("bk_irn_leap", "Spring Heels", "While wielding a two-handed axe, jumping costs 5% less stamina.", "CapeFeather", Bonus.Jump, 0.05f), new TreeWeb.Node("bk_irn_hold", "Unmoved", "While wielding a two-handed axe, nothing smaller than a troll can throw you back.", "ArmorIronLegs")),
			new TreeWeb.Lane("bk_endure", "Steady Work", new TreeWeb.Node("bk_stw_deep", "Deeper Draught", "Long Work restores 10 health and 14 stamina instead of 4 and 6.", "MeadHealthMedium"), new TreeWeb.Node("bk_stw_quick", "Quick Recovery", "Long Work's cooldown falls from 8 seconds to 5.", "Flint"), new TreeWeb.Node("bk_stw_heart", "Hot Blood", "While wielding a two-handed axe, normal health regeneration is 5% faster.", "MeadHealthMinor", Bonus.Health, 0.05f), new TreeWeb.Node("bk_stw_hard", "Hard Labour", "While wielding a two-handed axe, take another 5% less damage from everything.", "HelmetDrake")),
			new TreeWeb.Lane("bk_endure", "Thick Hide", new TreeWeb.Node("bk_thk_bone", "Braced Bones", "While wielding a two-handed axe, take another 3% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("bk_thk_wind", "Steady Breath", "While wielding a two-handed axe, normal stamina regeneration is another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("bk_thk_skin", "Thick Skin", "While wielding a two-handed axe, gain 2 armour.", "ArmorLeatherChest", Bonus.Armour, 2f), new TreeWeb.Node("bk_thk_death", "Death Refuses", "Survive an enemy hit below 30% health to restore 10 health, and for 4 seconds your two-handed axe attacks cost no stamina. Every 45 seconds.", "MeadHealthMedium")),
			new TreeWeb.Lane("bk_sunder", "Tremor", new TreeWeb.Node("bk_trm_wide", "Wider Tremor", "Sunder reaches 12 metres instead of 8.", "CapeLox"), new TreeWeb.Node("bk_trm_deep", "Deep Tremor", "Sunder hits 30% harder and leaves 5 Fractures instead of 3.", "Obsidian"), new TreeWeb.Node("bk_trm_quick", "Second Blow", "Sunder's cooldown falls from 45 seconds to 32.", "MeadStaminaMedium"), new TreeWeb.Node("bk_trm_rout", "Rout", "Enemies thrown back by Sunder lose track of you for 4 seconds and cannot regenerate health for 8.", "TrophySkeleton")),
			new TreeWeb.Lane("bk_sunder", "Quarryman", new TreeWeb.Node("bk_qry_edge", "Quarryman's Edge", "Two-handed axes deal another 3% more damage.", "Bronze", Bonus.Damage, 0.03f), new TreeWeb.Node("bk_qry_weight", "Quarryman's Weight", "Two-handed axe hits cause another 5% more stagger.", "Copper", Bonus.FistStagger, 0.05f), new TreeWeb.Node("bk_qry_wind", "Quarryman's Breath", "While wielding a two-handed axe, normal stamina regeneration is another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("bk_qry_take", "Take What You Need", "A two-handed axe kill restores 8 stamina while you carry more than half your weight limit. Every 10 seconds.", "MeadStaminaMedium"))
		};

		private static readonly TreeWeb.Node[] BreakerAll = TreeWeb.Flatten(BreakerNodes, BreakerLanes);

		private static readonly TreeWeb.Node[] HighlanderNodes = new TreeWeb.Node[25]
		{
			new TreeWeb.Node("hl_grip", "Two-Handed Grip", "Two-handed sword attacks cost 3% less stamina. Highlander combat talents require a two-handed sword in hand.", "Wood", Bonus.AttackCost, 0.03f),
			new TreeWeb.Node("hl_edge", "Keen Edge", "Two-handed swords deal 3% more damage.", "Bronze", Bonus.Damage, 0.03f),
			new TreeWeb.Node("hl_weight", "Weighted Blade", "Two-handed sword hits cause 6% more stagger.", "Copper", Bonus.FistStagger, 0.06f),
			new TreeWeb.Node("hl_hone", "Honed Edge", "Two-handed swords deal another 4% more damage.", "Iron", Bonus.Damage, 0.04f),
			new TreeWeb.Node("hl_execute", "Execution", "A two-handed sword hit below 30% health deals 60% extra, and finishes outright below 10%. A boss cannot be finished and takes 25% instead. Every 6 seconds.", "THSwordKrom"),
			new TreeWeb.Node("hl_control", "Controlled Grip", "Two-handed sword attacks cost another 3% less stamina.", "LeatherScraps", Bonus.AttackCost, 0.03f),
			new TreeWeb.Node("hl_breath", "Fighting Breath", "While wielding a two-handed sword, normal stamina regeneration is 4% faster.", "MeadStaminaMinor", Bonus.Regen, 0.04f),
			new TreeWeb.Node("hl_pace", "Measured Pace", "Two-handed sword attacks cost another 4% less stamina.", "Bronze", Bonus.AttackCost, 0.04f),
			new TreeWeb.Node("hl_heft", "Heft", "A two-handed sword hit landed at least 2 seconds after your last attack is a measured blow: 40% extra damage and double stagger.", "TrophyBoar"),
			new TreeWeb.Node("hl_brace", "Braced Stance", "While wielding a two-handed sword, take 5% less stagger buildup.", "HelmetLeather", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("hl_drive", "Driving Blade", "Two-handed sword hits cause another 5% more stagger.", "Iron", Bonus.FistStagger, 0.05f),
			new TreeWeb.Node("hl_temper", "Tempered Steel", "Two-handed swords deal another 2% more damage.", "Iron", Bonus.Damage, 0.02f),
			new TreeWeb.Node("hl_mortal", "Mortal Wound", "A two-handed sword hit marks the enemy for 10 seconds: it stops regenerating, takes 10% more from you, and its Execution line rises 5 points. Every 8 seconds.", "Bloodbag"),
			new TreeWeb.Node("hl_rooted", "Rooted Stance", "While wielding a two-handed sword, take another 5% less stagger buildup.", "ArmorLeatherLegs", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("hl_force", "Heavy Hands", "Two-handed sword hits cause another 5% more stagger.", "Iron", Bonus.FistStagger, 0.05f),
			new TreeWeb.Node("hl_poise", "Sure Footing", "While wielding a two-handed sword, take another 5% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("hl_break", "Break Them Open", "A two-handed sword hit on a staggered enemy deals 35% extra damage and restores 8 stamina. Once every 6 seconds.", "MaceIron"),
			new TreeWeb.Node("hl_recover", "Battle Hardened", "Gain 5 maximum health while this class is active. Adds to food bonuses; does not refill health.", "MeadHealthMinor", Bonus.MaximumHealth, 5f),
			new TreeWeb.Node("hl_lungs", "Deep Breath", "Gain 5 maximum stamina while this class is active. Adds to food bonuses; does not refill stamina.", "MeadStaminaMinor", Bonus.MaximumStamina, 5f),
			new TreeWeb.Node("hl_step", "Sidestep", "While wielding a two-handed sword, dodging costs 5% less stamina.", "DeerHide", Bonus.Dodge, 0.05f),
			new TreeWeb.Node("hl_resolve", "Grim Resolve", "Four seconds without dodging sets your stance: gain 3 armour and take 10% less stagger buildup until you dodge again. Requires a two-handed sword in hand.", "ArmorIronChest"),
			new TreeWeb.Node("hl_stride", "Long Stride", "While wielding a two-handed sword, running costs 5% less stamina.", "CapeDeerHide", Bonus.Run, 0.05f),
			new TreeWeb.Node("hl_ready", "Ready Footwork", "While wielding a two-handed sword, dodging costs another 5% less stamina.", "ArmorLeatherLegs", Bonus.Dodge, 0.05f),
			new TreeWeb.Node("hl_bear", "Bear the Weight", "Armour weighs on you 40% less: that share of the movement penalty from your equipped armour is ignored.", "ArmorIronLegs", Bonus.Burden, 0.4f),
			new TreeWeb.Node("hl_headsman", "The Headsman's Swing", "Drop the greatsword on one enemy within 4 metres: 45 slash, heavy stagger. Doubles below 35% health, finishes below 15%. 40 stamina, every 18 seconds.", "SwordMistwalker")
		};

		private static readonly TreeWeb.Lane[] HighlanderLanes = new TreeWeb.Lane[12]
		{
			new TreeWeb.Lane("hl_execute", "Headsman", new TreeWeb.Node("hl_exe_low", "Wider Mercy", "Execution triggers below 40% health instead of 30%.", "KnifeSilver"), new TreeWeb.Node("hl_exe_deep", "Heavier Blow", "Execution deals 90% extra damage instead of 60%.", "Iron"), new TreeWeb.Node("hl_exe_quick", "Practised Hand", "Execution's cooldown falls from 6 to 4 seconds.", "Flint"), new TreeWeb.Node("hl_exe_edge", "Headsman's Edge", "Two-handed swords deal another 2% more damage.", "Bronze", Bonus.Damage, 0.02f), new TreeWeb.Node("hl_exe_free", "No Second Blow", "Execution has no cooldown at all against an enemy already below 15% health. There is no waiting to finish something that is finished.", "SwordBlackmetal")),
			new TreeWeb.Lane("hl_execute", "Grim Harvest", new TreeWeb.Node("hl_har_heart", "Hot Blood", "While wielding a two-handed sword, normal health regeneration is 5% faster.", "MeadHealthMinor", Bonus.Health, 0.05f), new TreeWeb.Node("hl_har_take", "Blood Price", "An Execution restores 10 health and 15 stamina. Once every 10 seconds.", "MeadHealthMedium"), new TreeWeb.Node("hl_har_fear", "The Sight of It", "An Execution staggers every visible enemy within 5 metres.", "TrophySkeleton"), new TreeWeb.Node("hl_har_reap", "The Reaping", "An Execution also strikes every enemy within 6 metres that is itself below the Execution line, for 40% of the hit, capped at 20 each.", "SwordSilver")),
			new TreeWeb.Lane("hl_heft", "Measured", new TreeWeb.Node("hl_hft_deep", "Full Weight", "A measured blow deals 65% extra damage instead of 40%.", "Stone"), new TreeWeb.Node("hl_hft_quick", "Short Wind", "A measured blow needs 1.5 seconds since your last attack instead of 2.", "TrophyBoar"), new TreeWeb.Node("hl_hft_break", "Through the Guard", "A measured blow cannot be blocked.", "SwordIron"), new TreeWeb.Node("hl_hft_mark", "Splitting Blow", "A measured blow raises the enemy's Execution line by 10 points for 8 seconds.", "AtgeirIron")),
			new TreeWeb.Lane("hl_heft", "Long Reach", new TreeWeb.Node("hl_rch_grip", "Sure Grip", "Two-handed sword attacks cost another 2% less stamina.", "LeatherScraps", Bonus.AttackCost, 0.02f), new TreeWeb.Node("hl_rch_far", "The Long Edge", "Two-handed sword hits on an enemy more than 2.5 metres away deal 12% more damage.", "SpearElderbark"), new TreeWeb.Node("hl_rch_wind", "Deep Lungs", "While wielding a two-handed sword, normal stamina regeneration is another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("hl_rch_room", "Room to Swing", "While no enemy stands within 1.5 metres of you, two-handed sword attacks cost 20% less stamina.", "CapeDeerHide")),
			new TreeWeb.Lane("hl_mortal", "Hemorrhage", new TreeWeb.Node("hl_mor_long", "Lingering Wound", "A mortal wound lasts 16 seconds instead of 10.", "Bloodbag"), new TreeWeb.Node("hl_mor_quick", "Quick Work", "Mortal Wound's cooldown falls from 8 to 5 seconds.", "Flint"), new TreeWeb.Node("hl_mor_deep", "Deep Cut", "A mortally wounded enemy takes 18% more damage from you instead of 10%.", "KnifeCopper"), new TreeWeb.Node("hl_mor_edge", "Serrated Edge", "Two-handed swords deal another 2% more damage.", "Iron", Bonus.Damage, 0.02f), new TreeWeb.Node("hl_mor_low", "Marked for Death", "A mortal wound raises the enemy's Execution line by 15 points instead of 5.", "WolfFang")),
			new TreeWeb.Lane("hl_mortal", "Carver", new TreeWeb.Node("hl_crv_weight", "Cleaving Weight", "Two-handed sword hits cause another 5% more stagger.", "Copper", Bonus.FistStagger, 0.05f), new TreeWeb.Node("hl_crv_hide", "Scarred Hide", "While wielding a two-handed sword, gain 3 armour.", "ArmorLeatherChest", Bonus.Armour, 3f), new TreeWeb.Node("hl_crv_feast", "Butcher's Feast", "Killing a mortally wounded enemy restores 12 health and readies Mortal Wound at once. Once every 15 seconds.", "RawMeat"), new TreeWeb.Node("hl_crv_scar", "Scar Tissue", "While wielding a two-handed sword, gain 1 armour for every tenth of your maximum health that is missing, up to 6.", "WolfPelt")),
			new TreeWeb.Lane("hl_break", "Shatter", new TreeWeb.Node("hl_brk_deep", "Splitting Force", "Break Them Open deals 55% extra damage instead of 35%.", "MaceBronze"), new TreeWeb.Node("hl_brk_quick", "Quick Recovery", "Break Them Open's cooldown falls from 6 to 4 seconds.", "Flint"), new TreeWeb.Node("hl_brk_stam", "Second Wind", "Break Them Open restores 14 stamina instead of 8.", "MeadStaminaMedium"), new TreeWeb.Node("hl_brk_hold", "Keep Them Down", "Your two-handed sword hits on a staggered enemy that is also mortally wounded deal a further 20% extra damage.", "BattleaxeCrystal")),
			new TreeWeb.Lane("hl_break", "Ironbound", new TreeWeb.Node("hl_irn_back", "Broad Back", "While wielding a two-handed sword, gain 2 armour.", "ArmorLeatherChest", Bonus.Armour, 2f), new TreeWeb.Node("hl_irn_gut", "Iron Gut", "While wielding a two-handed sword, take another 3% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("hl_irn_bear", "Plated Stride", "Armour weighs on you another 40% less. With Bear the Weight, four-fifths of the penalty is gone.", "ArmorIronChest", Bonus.Burden, 0.4f), new TreeWeb.Node("hl_irn_stand", "Set Against It", "While Grim Resolve holds, enemy hits cannot push you back.", "ArmorIronLegs")),
			new TreeWeb.Lane("hl_resolve", "Unflinching", new TreeWeb.Node("hl_unf_deep", "Deeper Resolve", "Grim Resolve grants 6 armour instead of 3.", "ArmorIronChest"), new TreeWeb.Node("hl_unf_quick", "Settled Stance", "Grim Resolve sets after 2.5 seconds without dodging instead of 4.", "ArmorBronzeLegs"), new TreeWeb.Node("hl_unf_stand", "Unmoved", "While Grim Resolve holds you take no stagger buildup at all.", "HelmetIron"), new TreeWeb.Node("hl_unf_edge", "Planted Edge", "While Grim Resolve holds, your two-handed sword hits deal 10% more damage.", "SwordIron")),
			new TreeWeb.Lane("hl_resolve", "Last Rites", new TreeWeb.Node("hl_rit_heart", "Strong Heart", "While wielding a two-handed sword, normal health regeneration is another 5% faster.", "MeadHealthMinor", Bonus.Health, 0.05f), new TreeWeb.Node("hl_rit_gut", "Braced Bones", "While wielding a two-handed sword, take another 3% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("hl_rit_wind", "Steady Breath", "While wielding a two-handed sword, normal stamina regeneration is another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("hl_rit_death", "Last Rites", "Survive an enemy hit below 30% health to restore 10 health, and for 4 seconds your two-handed sword attacks cost no stamina. Every 45 seconds.", "MeadHealthMedium")),
			new TreeWeb.Lane("hl_headsman", "The Block", new TreeWeb.Node("hl_hds_edge", "Sharpened Block", "The Headsman's Swing deals 40% more damage.", "Iron"), new TreeWeb.Node("hl_hds_quick", "Practised Drop", "The Headsman's Swing's cooldown falls from 18 to 13 seconds.", "LeatherScraps"), new TreeWeb.Node("hl_hds_low", "Clean Cut", "The Headsman's Swing finishes an enemy outright below 25% health instead of 15%.", "SwordBlackmetal"), new TreeWeb.Node("hl_hds_wide", "The Scaffold", "The Headsman's Swing also staggers every visible enemy within 4 metres of its target.", "TrophyGreydwarf")),
			new TreeWeb.Lane("hl_headsman", "Gravedigger", new TreeWeb.Node("hl_grv_edge", "Gravedigger's Edge", "Two-handed swords deal another 3% more damage.", "Bronze", Bonus.Damage, 0.03f), new TreeWeb.Node("hl_grv_weight", "Gravedigger's Weight", "Two-handed sword hits cause another 5% more stagger.", "Copper", Bonus.FistStagger, 0.05f), new TreeWeb.Node("hl_grv_wind", "Gravedigger's Breath", "While wielding a two-handed sword, normal stamina regeneration is another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("hl_grv_toll", "The Toll", "A two-handed sword kill readies Execution at once and makes your next hit within 6 seconds a measured blow. Every 12 seconds; cannot chain.", "TrophySkeleton"))
		};

		private static readonly TreeWeb.Node[] HighlanderAll = TreeWeb.Flatten(HighlanderNodes, HighlanderLanes);

		private static readonly TreeWeb.Node[] RogueNodes = new TreeWeb.Node[25]
		{
			new TreeWeb.Node("q_root", "Lightfoot", "Sneaking costs 5% less stamina.", "LeatherScraps", Bonus.Sneak, 0.05f),
			new TreeWeb.Node("q_shade", "Shade", "While crouched, enemies notice you from 5% closer.", "CapeTrollHide", Bonus.Stealth, 0.05f),
			new TreeWeb.Node("q_quiet", "Quiet Soles", "Make 10% less noise.", "DeerHide", Bonus.Noise, 0.1f),
			new TreeWeb.Node("q_edge", "Hidden Edge", "Melee attacks deal 4% more damage.", "KnifeFlint", Bonus.Damage, 0.04f),
			new TreeWeb.Node("q_ambush", "Ambush", "Your melee hit against an unalerted enemy deals 35% more damage. Once every 10 seconds.", "KnifeCopper"),
			new TreeWeb.Node("q_stalk", "Stalker", "Sneaking costs another 5% less stamina.", "CapeTrollHide", Bonus.Sneak, 0.05f),
			new TreeWeb.Node("q_reach", "Patient Hunter", "While crouched, enemies notice you from another 5% closer.", "GreydwarfEye", Bonus.Stealth, 0.05f),
			new TreeWeb.Node("q_cut", "Fine Edge", "Melee attacks deal another 3% more damage.", "KnifeCopper", Bonus.Damage, 0.03f),
			new TreeWeb.Node("q_vein", "Open Vein", "A melee hit from behind or against an unalerted enemy causes a 6-second bleed: 20% of the hit, capped at 12. Once every 8 seconds.", "Bloodbag"),
			new TreeWeb.Node("q_step", "Sidestep", "Dodging costs 5% less stamina.", "ArmorLeatherLegs", Bonus.Dodge, 0.05f),
			new TreeWeb.Node("q_stride", "Swift Feet", "Running costs 5% less stamina.", "CapeDeerHide", Bonus.Run, 0.05f),
			new TreeWeb.Node("q_balance", "Balance", "Take 5% less stagger buildup.", "HelmetLeather", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("q_counter", "Counterstep", "Dodge, then land a melee hit within 5 seconds for 25% more damage. Once every 10 seconds.", "KnifeSilver"),
			new TreeWeb.Node("q_shadow", "Shadow Steps", "Make another 10% less noise.", "Feathers", Bonus.Noise, 0.1f),
			new TreeWeb.Node("q_breath", "Held Breath", "Stamina regenerates 3% faster.", "MeadStaminaMinor", Bonus.Regen, 0.03f),
			new TreeWeb.Node("q_crouch", "Low Profile", "Sneaking costs another 5% less stamina.", "LeatherScraps", Bonus.Sneak, 0.05f),
			new TreeWeb.Node("q_vanish", "Vanish", "Break away. Enemies within 30 metres lose you. For 6 seconds you are 50% better concealed and 75% quieter. 30 stamina, every 45 seconds.", "CapeTrollHide"),
			new TreeWeb.Node("q_escape", "Escape Route", "Running costs another 5% less stamina.", "CapeWolf", Bonus.Run, 0.05f),
			new TreeWeb.Node("q_roll", "Practised Roll", "Dodging costs another 5% less stamina.", "ArmorTrollLeatherLegs", Bonus.Dodge, 0.05f),
			new TreeWeb.Node("q_recover", "Survivor", "Gain 5 maximum health while this class is active. Adds to food bonuses; does not refill health.", "MeadHealthMinor", Bonus.MaximumHealth, 5f),
			new TreeWeb.Node("q_survive", "Narrow Escape", "Survive an enemy hit below 30% health to restore 4 health and 12 stamina. Once every 45 seconds; cannot save a lethal hit.", "MeadHealthMedium"),
			new TreeWeb.Node("q_leap", "Light Landing", "Jumping costs 10% less stamina.", "Feathers", Bonus.Jump, 0.1f),
			new TreeWeb.Node("q_poised", "Poised", "Take another 5% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("q_air", "Second Breath", "Gain 5 maximum stamina while this class is active. Adds to food bonuses; does not refill stamina.", "MeadStaminaMinor", Bonus.MaximumStamina, 5f),
			new TreeWeb.Node("q_fall", "Featherfall", "Absorb up to 20 fall damage once every 30 seconds. Larger falls can still hurt or kill you.", "CapeFeather")
		};

		private static readonly TreeWeb.Node[] MonkNodes = new TreeWeb.Node[25]
		{
			new TreeWeb.Node("n_root", "Empty Hands", "Bare-fist attacks gain 5 blunt damage. Claws are the only weapons a Monk may equip, and never a shield. Armour and non-combat tools remain usable.", "TrophyBoar", Bonus.Fist, 5f),
			new TreeWeb.Node("n_knuckle", "Conditioned Knuckles", "Bare fists gain another 3 blunt damage.", "Stone", Bonus.Fist, 3f),
			new TreeWeb.Node("n_breath", "Measured Breath", "Stamina regenerates 3% faster.", "MeadStaminaMinor", Bonus.Regen, 0.03f),
			new TreeWeb.Node("n_form", "Economy of Motion", "Bare-fist attacks cost 5% less stamina.", "Flint", Bonus.AttackCost, 0.05f),
			new TreeWeb.Node("n_third", "Third Bell", "Every third bare-fist hit on the same enemy within 8 seconds deals 50% more damage and double stagger. Changing targets resets the sequence.", "Bronze"),
			new TreeWeb.Node("n_step", "Open Stance", "Dodging costs 5% less stamina.", "ArmorLeatherLegs", Bonus.Dodge, 0.05f),
			new TreeWeb.Node("n_stride", "Wind Step", "Running costs 5% less stamina.", "CapeDeerHide", Bonus.Run, 0.05f),
			new TreeWeb.Node("n_force", "Firm Palm", "Bare-fist attacks cause 12% more stagger.", "Stone", Bonus.FistStagger, 0.12f),
			new TreeWeb.Node("n_palm", "Driving Palm", "Dodge, then punch an enemy within 5 seconds to knock it back. Once every 8 seconds. Large enemies resist through their mass.", "TrophyGreydwarf"),
			new TreeWeb.Node("n_flow", "Flow", "Bare-fist attacks cost another 5% less stamina.", "Flint", Bonus.AttackCost, 0.05f),
			new TreeWeb.Node("n_spring", "Spring Step", "Jumping costs 10% less stamina.", "Feathers", Bonus.Jump, 0.1f),
			new TreeWeb.Node("n_still", "Still Mind", "Gain 5 maximum stamina while this class is active. Adds to food bonuses; does not refill stamina.", "MeadStaminaMinor", Bonus.MaximumStamina, 5f),
			new TreeWeb.Node("n_return", "Returning Breath", "Dodge, then land a bare-fist hit within 5 seconds to regain 10 stamina. Once every 10 seconds.", "MeadStaminaMedium"),
			new TreeWeb.Node("n_mend", "Vital Breath", "Gain 5 maximum health while this class is active. Adds to food bonuses; does not refill health.", "MeadHealthMinor", Bonus.MaximumHealth, 5f),
			new TreeWeb.Node("n_resolve", "Resolve", "Take 5% less stagger buildup.", "HelmetLeather", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("n_body", "Conditioned Body", "Gain 3 armour.", "ArmorLeatherChest", Bonus.Armour, 3f),
			new TreeWeb.Node("n_clear", "Clear Mind", "Gather yourself on command. For 8 seconds your bare-fist attacks cost no stamina, deal 20% more damage and cause double stagger. 25 stamina, every 30 seconds.", "MeadHealthMedium"),
			new TreeWeb.Node("n_iron", "Iron Knuckles", "Bare fists gain another 2 blunt damage.", "Iron", Bonus.Fist, 2f),
			new TreeWeb.Node("n_poise", "Poise", "Take another 5% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.05f),
			new TreeWeb.Node("n_reach", "Firm Foundation", "Bare-fist attacks cause another 10% more stagger.", "Bronze", Bonus.FistStagger, 0.1f),
			new TreeWeb.Node("n_sweep", "Sweeping Palm", "Every third same-target punch also strikes one visible enemy within 3m for 35% of the punch, capped at 8 blunt damage. 8-second cooldown; cannot chain.", "TrophySkeleton"),
			new TreeWeb.Node("n_guard", "Open Guard", "Blocking with bare hands costs 10% less stamina.", "Stone", Bonus.Block, 0.1f),
			new TreeWeb.Node("n_patient", "Patient Guard", "Gain another 2 armour.", "ArmorLeatherChest", Bonus.Armour, 2f),
			new TreeWeb.Node("n_ready", "Ready Stance", "Dodging costs another 5% less stamina.", "ArmorLeatherLegs", Bonus.Dodge, 0.05f),
			new TreeWeb.Node("n_parry", "Iron Palm", "A successful bare-hand block restores 6 stamina and empowers your next punch within 5 seconds by 30%. Once every 10 seconds.", "Iron")
		};

		private static readonly TreeWeb.Lane[] RogueLanes = new TreeWeb.Lane[12]
		{
			new TreeWeb.Lane("q_ambush", "Assassin", new TreeWeb.Node("q_amb_edge", "Killing Edge", "Ambush's opening strike deals 50% more damage instead of 35%.", "KnifeCopper"), new TreeWeb.Node("q_amb_quick", "No Second Chance", "Ambush's cooldown falls from 10 to 7 seconds.", "KnifeFlint"), new TreeWeb.Node("q_amb_cut", "Practised Cut", "Melee attacks deal another 3% more damage.", "Bronze", Bonus.Damage, 0.03f), new TreeWeb.Node("q_amb_mark", "Mark the Throat", "An Ambush strike marks its enemy for 8 seconds; your melee hits on the marked enemy deal 15% more damage. One mark at a time, and a new Ambush moves it.", "KnifeSilver")),
			new TreeWeb.Lane("q_ambush", "Prowler", new TreeWeb.Node("q_prowl_soft", "Soft Ground", "Sneaking costs another 4% less stamina.", "LeatherScraps", Bonus.Sneak, 0.04f), new TreeWeb.Node("q_prowl_dim", "Dim Figure", "While crouched, enemies notice you from another 4% closer.", "GreydwarfEye", Bonus.Stealth, 0.04f), new TreeWeb.Node("q_prowl_hush", "Hushed Step", "Make another 5% less noise.", "Feathers", Bonus.Noise, 0.05f), new TreeWeb.Node("q_prowl_unseen", "Never There", "A melee kill breaks contact: for 4 seconds you are 40% better concealed and 50% quieter. Once every 20 seconds.", "CapeTrollHide")),
			new TreeWeb.Lane("q_vein", "Bloodletter", new TreeWeb.Node("q_blood_deep", "Deep Cut", "Open Vein's wound is capped at 18 instead of 12.", "Bloodbag"), new TreeWeb.Node("q_blood_keen", "Serrated Edge", "Melee attacks deal another 2% more damage.", "KnifeCopper", Bonus.Damage, 0.02f), new TreeWeb.Node("q_blood_quick", "Quick Work", "Open Vein's cooldown falls from 8 to 6 seconds.", "KnifeFlint"), new TreeWeb.Node("q_blood_long", "Slow Bleed", "Open Vein's wound runs four seconds longer at the same rate: five ticks instead of three.", "Bloodbag"), new TreeWeb.Node("q_blood_hunt", "Scent of Blood", "Your melee hits on an enemy already bleeding from Open Vein deal 15% more damage.", "KnifeSilver")),
			new TreeWeb.Lane("q_vein", "Cutthroat", new TreeWeb.Node("q_cut_grip", "Reverse Grip", "Melee attacks deal another 3% more damage.", "KnifeFlint", Bonus.Damage, 0.03f), new TreeWeb.Node("q_cut_side", "Blindside", "While crouched, enemies notice you from another 3% closer.", "CapeTrollHide", Bonus.Stealth, 0.03f), new TreeWeb.Node("q_cut_breath", "Steady Hand", "Stamina regenerates another 2% faster.", "MeadStaminaMedium", Bonus.Regen, 0.02f), new TreeWeb.Node("q_cut_finish", "Finish It", "A melee hit on an enemy below 25% health deals 25% more damage. Once every 8 seconds.", "KnifeSilver")),
			new TreeWeb.Lane("q_counter", "Riposte", new TreeWeb.Node("q_rip_edge", "Sharper Answer", "Counterstep's empowered hit deals 40% more damage instead of 25%.", "KnifeCopper"), new TreeWeb.Node("q_rip_swift", "Quick Feet", "Counterstep's cooldown falls from 10 to 7 seconds.", "ArmorLeatherLegs"), new TreeWeb.Node("q_rip_roll", "Loose Roll", "Dodging costs another 3% less stamina.", "ArmorTrollLeatherLegs", Bonus.Dodge, 0.03f), new TreeWeb.Node("q_rip_twin", "Twin Answer", "Counterstep answers twice: the next two melee hits inside its window are both empowered.", "KnifeSilver")),
			new TreeWeb.Lane("q_counter", "Footpad", new TreeWeb.Node("q_foot_stride", "Fleet Stride", "Running costs another 3% less stamina.", "CapeDeerHide", Bonus.Run, 0.03f), new TreeWeb.Node("q_foot_poise", "Loose Shoulders", "Take another 3% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("q_foot_jump", "Light Feet", "Jumping costs another 5% less stamina.", "CapeFeather", Bonus.Jump, 0.05f), new TreeWeb.Node("q_foot_push", "Shoulder Check", "Dodge, then land a melee hit within 5 seconds to add a strong knockback impulse. Once every 12 seconds. Large enemies resist it through their normal mass.", "HelmetLeather")),
			new TreeWeb.Lane("q_vanish", "Vanishing Act", new TreeWeb.Node("q_van_deep", "Deeper Shadow", "Vanish conceals you for 3 seconds longer.", "CapeTrollHide"), new TreeWeb.Node("q_van_quiet", "Silent Tread", "Make another 5% less noise.", "Feathers", Bonus.Noise, 0.05f), new TreeWeb.Node("q_van_swift", "Quick Exit", "Vanish's cooldown falls from 45 to 35 seconds.", "ArmorTrollLeatherLegs"), new TreeWeb.Node("q_van_twin", "Second Shadow", "Vanish sells two openings instead of one: the first two melee hits inside its window each count as a hit on an unalerted enemy.", "KnifeSilver")),
			new TreeWeb.Lane("q_vanish", "Slipstream", new TreeWeb.Node("q_slip_roll", "Rolling Start", "Dodging costs another 3% less stamina.", "ArmorLeatherLegs", Bonus.Dodge, 0.03f), new TreeWeb.Node("q_slip_shade", "Dimmer Shade", "While crouched, enemies notice you from another 3% closer.", "GreydwarfEye", Bonus.Stealth, 0.03f), new TreeWeb.Node("q_slip_breath", "Even Breath", "Stamina regenerates another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("q_slip", "Slipaway", "Dodging grants 5 seconds of 30% better crouched concealment and 50% less noise. Once every 10 seconds. Enemies do not forget you automatically.", "CapeTrollHide")),
			new TreeWeb.Lane("q_survive", "Survivor", new TreeWeb.Node("q_surv_deep", "Second Wind", "Narrow Escape restores 8 health instead of 4.", "MeadHealthMedium"), new TreeWeb.Node("q_surv_swift", "Practised Escape", "Narrow Escape's cooldown falls from 45 to 30 seconds.", "CapeWolf"), new TreeWeb.Node("q_surv_heart", "Quiet Heart", "Normal health regeneration is 5% faster.", "MeadHealthMinor", Bonus.Health, 0.05f), new TreeWeb.Node("q_surv_wind", "Full Lungs", "Narrow Escape restores 20 stamina instead of 12.", "MeadStaminaMedium"), new TreeWeb.Node("q_surv_smoke", "Smoke and Mirrors", "Narrow Escape also breaks contact: enemies within 20 metres lose track of you, and for 4 seconds you are 40% better concealed and 50% quieter.", "CapeTrollHide")),
			new TreeWeb.Lane("q_survive", "Hardened", new TreeWeb.Node("q_hard_skin", "Toughened Leathers", "Gain 3 armour.", "ArmorLeatherChest", Bonus.Armour, 3f), new TreeWeb.Node("q_hard_grit", "Grit", "Take another 3% less stagger buildup.", "HelmetLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("q_hard_wind", "Long Wind", "Stamina regenerates another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("q_hard_corner", "Cornered", "While below 30% health your melee attacks deal 15% more damage and your dodges cost no stamina.", "MeadHealthMedium")),
			new TreeWeb.Lane("q_fall", "Freerunner", new TreeWeb.Node("q_free_light", "Lighter Landing", "Featherfall absorbs up to 30 fall damage instead of 20.", "CapeFeather"), new TreeWeb.Node("q_free_quick", "Quick Recovery", "Featherfall's cooldown falls from 30 to 20 seconds.", "ArmorTrollLeatherLegs"), new TreeWeb.Node("q_free_leap", "Long Leap", "Jumping costs another 5% less stamina.", "Feathers", Bonus.Jump, 0.05f), new TreeWeb.Node("q_free_roll", "Roll With It", "An absorbed fall becomes momentum: restore 15 stamina, and your next melee hit within 5 seconds deals 20% more damage.", "CapeLox")),
			new TreeWeb.Lane("q_fall", "Dropfall", new TreeWeb.Node("q_drop_leap", "Spring Loaded", "Jumping costs another 5% less stamina.", "CapeFeather", Bonus.Jump, 0.05f), new TreeWeb.Node("q_drop_poise", "Loose Knees", "Take another 3% less stagger buildup.", "ArmorLeatherLegs", Bonus.Stagger, 0.03f), new TreeWeb.Node("q_drop_quiet", "Quiet Landing", "Make another 5% less noise.", "DeerHide", Bonus.Noise, 0.05f), new TreeWeb.Node("q_drop_strike", "Falling Blade", "A melee hit landed while you are off the ground deals 30% more damage and counts as a hit on an unalerted enemy. Once every 10 seconds.", "KnifeSilver"))
		};

		private static readonly TreeWeb.Lane[] MonkLanes = new TreeWeb.Lane[12]
		{
			new TreeWeb.Lane("n_third", "Cadence", new TreeWeb.Node("n_bell_deep", "Struck Bell", "Third Bell's hit deals 75% more damage instead of 50%.", "Bronze"), new TreeWeb.Node("n_bell_quick", "Quickening", "Third Bell's chain holds for 12 seconds between hits instead of 8.", "Flint"), new TreeWeb.Node("n_bell_knuck", "Seasoned Knuckles", "Bare fists gain another 2 blunt damage.", "Stone", Bonus.Fist, 2f), new TreeWeb.Node("n_bell_carry", "Carry the Rhythm", "Your combination no longer resets when you change enemies. Only the chain window can break it.", "TrophyBoar")),
			new TreeWeb.Lane("n_third", "Conditioning", new TreeWeb.Node("n_cond_wrap", "Wrapped Hands", "Bare-fist attacks cost another 3% less stamina.", "LeatherScraps", Bonus.AttackCost, 0.03f), new TreeWeb.Node("n_cond_bone", "Bone Density", "Bare fists gain another 2 blunt damage.", "Iron", Bonus.Fist, 2f), new TreeWeb.Node("n_cond_wind", "Working Lungs", "Stamina regenerates another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("n_cond_break", "Break the Guard", "The third hit of your combination cannot be blocked.", "Iron")),
			new TreeWeb.Lane("n_palm", "Driving Force", new TreeWeb.Node("n_palm_force", "Full Extension", "Driving Palm's knockback is half again as strong.", "Stone"), new TreeWeb.Node("n_palm_quick", "Short Step", "Driving Palm's cooldown falls from 8 to 6 seconds.", "ArmorLeatherLegs"), new TreeWeb.Node("n_palm_stance", "Rooted Palm", "Bare-fist attacks cause another 5% more stagger.", "Bronze", Bonus.FistStagger, 0.05f), new TreeWeb.Node("n_palm_wave", "Shockpalm", "Driving Palm also throws back every other enemy within 3 metres of the one you hit. Large enemies resist through their mass.", "TrophyGreydwarf")),
			new TreeWeb.Lane("n_palm", "Wind Step", new TreeWeb.Node("n_wind_light", "Light Step", "Dodging costs another 3% less stamina.", "ArmorLeatherLegs", Bonus.Dodge, 0.03f), new TreeWeb.Node("n_wind_run", "Running Form", "Running costs another 5% less stamina.", "CapeDeerHide", Bonus.Run, 0.05f), new TreeWeb.Node("n_wind_leap", "High Step", "Jumping costs another 5% less stamina.", "CapeFeather", Bonus.Jump, 0.05f), new TreeWeb.Node("n_wind_crash", "Crashing Step", "Dodge, then land a bare-fist hit within 5 seconds and it lands as the third bell of your combination, whatever the count stood at. Once every 12 seconds.", "TrophySkeleton")),
			new TreeWeb.Lane("n_return", "Breathing", new TreeWeb.Node("n_ret_deep", "Deep Breath", "Returning Breath restores 18 stamina instead of 10.", "MeadStaminaMedium"), new TreeWeb.Node("n_ret_quick", "Even Tempo", "Returning Breath's cooldown falls from 10 to 7 seconds.", "Flint"), new TreeWeb.Node("n_ret_flow", "Unbroken Flow", "Bare-fist attacks cost another 3% less stamina.", "LeatherScraps", Bonus.AttackCost, 0.03f), new TreeWeb.Node("n_ret_calm", "Settled Lungs", "Stamina regenerates another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("n_ret_tireless", "Tireless", "The Returning Breath hit makes your bare-fist attacks cost no stamina for 4 seconds.", "MeadStaminaMedium")),
			new TreeWeb.Lane("n_return", "Current", new TreeWeb.Node("n_cur_step", "Flowing Step", "Dodging costs another 3% less stamina.", "DeerHide", Bonus.Dodge, 0.03f), new TreeWeb.Node("n_cur_poise", "Unmoved", "Take another 3% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("n_cur_hand", "Loose Hands", "Bare-fist attacks cost another 2% less stamina.", "Flint", Bonus.AttackCost, 0.02f), new TreeWeb.Node("n_cur_redirect", "Redirect", "A bare-fist hit landed within 5 seconds of taking an enemy hit deals 20% more damage. Once every 10 seconds.", "ArmorLeatherChest")),
			new TreeWeb.Lane("n_clear", "Clarity", new TreeWeb.Node("n_clear_long", "Held Breath", "Clear Mind lasts 3 seconds longer.", "MeadStaminaMedium"), new TreeWeb.Node("n_clear_level", "Level Head", "Take another 3% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("n_clear_swift", "Quick Settle", "Clear Mind's cooldown falls from 30 to 22 seconds.", "Flint"), new TreeWeb.Node("n_clear_force", "Rushing Palm", "While Clear Mind holds, every bare-fist hit adds a strong knockback impulse. Large enemies resist through their mass.", "TrophyGreydwarf")),
			new TreeWeb.Lane("n_clear", "Stillness", new TreeWeb.Node("n_calm_body", "Settled Body", "Gain another 2 armour.", "ArmorLeatherChest", Bonus.Armour, 2f), new TreeWeb.Node("n_calm_guard", "Soft Guard", "Blocking with bare hands costs another 5% less stamina.", "Stone", Bonus.Block, 0.05f), new TreeWeb.Node("n_calm_heart", "Quiet Heart", "Normal health regeneration is 5% faster.", "MeadHealthMinor", Bonus.Health, 0.05f), new TreeWeb.Node("n_calm", "Inner Calm", "Survive an enemy hit below 30% health to restore 6 health. Once every 45 seconds; cannot save a lethal hit.", "MeadHealthMedium")),
			new TreeWeb.Lane("n_sweep", "Wide Arc", new TreeWeb.Node("n_sweep_reach", "Longer Reach", "Sweeping Palm reaches 5 metres instead of 3.", "Bronze"), new TreeWeb.Node("n_sweep_hard", "Heavier Sweep", "Sweeping Palm's secondary hit is capped at 14 instead of 8.", "Iron"), new TreeWeb.Node("n_sweep_quick", "Quick Sweep", "Sweeping Palm's cooldown falls from 8 to 6 seconds.", "Flint"), new TreeWeb.Node("n_sweep_stagger", "Sweeping Force", "Bare-fist attacks cause another 5% more stagger.", "Stone", Bonus.FistStagger, 0.05f), new TreeWeb.Node("n_sweep_circle", "Circle Sweep", "Sweeping Palm strikes up to three nearby enemies instead of one.", "TrophySkeleton")),
			new TreeWeb.Lane("n_sweep", "Uprooting", new TreeWeb.Node("n_root_force", "Rooted Force", "Bare-fist attacks cause another 5% more stagger.", "Copper", Bonus.FistStagger, 0.05f), new TreeWeb.Node("n_root_poise", "Deep Root", "Take another 3% less stagger buildup.", "HelmetLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("n_root_wind", "Working Breath", "Stamina regenerates another 2% faster.", "MeadStaminaMinor", Bonus.Regen, 0.02f), new TreeWeb.Node("n_root_topple", "Topple", "Your bare-fist hit on an enemy that is already staggering deals 30% more damage and throws it back. Once every 8 seconds.", "TrophyGreydwarf")),
			new TreeWeb.Lane("n_parry", "Open Guard", new TreeWeb.Node("n_parry_deep", "Steady Guard", "Iron Palm restores 12 stamina instead of 6.", "MeadStaminaMedium"), new TreeWeb.Node("n_parry_quick", "Ready Hands", "Iron Palm's cooldown falls from 10 to 7 seconds.", "Flint"), new TreeWeb.Node("n_parry_block", "Softer Guard", "Blocking with bare hands costs another 5% less stamina.", "Stone", Bonus.Block, 0.05f), new TreeWeb.Node("n_parry_turn", "Turn the Blow", "A successful bare-hand block also throws the attacker back, along with anything else within 3 metres. Large enemies resist through their mass.", "Iron")),
			new TreeWeb.Lane("n_parry", "Unshakeable", new TreeWeb.Node("n_shake_arm", "Toughened Arms", "Gain another 2 armour.", "ArmorLeatherChest", Bonus.Armour, 2f), new TreeWeb.Node("n_shake_poise", "Unshakeable", "Take another 3% less stagger buildup.", "HelmetTrollLeather", Bonus.Stagger, 0.03f), new TreeWeb.Node("n_shake_heart", "Steady Heart", "Normal health regeneration is another 5% faster.", "MeadHealthMinor", Bonus.Health, 0.05f), new TreeWeb.Node("n_shake_stand", "Immovable", "While no enemy has hit you for 5 seconds, you take no stagger buildup at all.", "ArmorLeatherChest"))
		};

		private static readonly TreeWeb.Node[] RogueAll = TreeWeb.Flatten(RogueNodes, RogueLanes);

		private static readonly TreeWeb.Node[] MonkAll = TreeWeb.Flatten(MonkNodes, MonkLanes);

		public static IReadOnlyList<Talent> Berserker { get; } = TreeWeb.Build(BerserkerNodes, BerserkerLanes);

		public static IReadOnlyList<ClassSpell> BreakerSpells { get; } = Array.AsReadOnly(new ClassSpell[1]
		{
			new ClassSpell("bk_sunder", "Sunder", "Stone", "drive the axe into the ground: up to 6 enemies near you take 22 blunt, and are staggered, thrown back and left fractured.", 0f, 45f, 22f, 6, SpellKind.Sunder, "breaker", 0f, 35f)
		});

		public static IReadOnlyList<Talent> Breaker { get; } = TreeWeb.Build(BreakerNodes, BreakerLanes);

		public static IReadOnlyList<ClassSpell> HighlanderSpells { get; } = Array.AsReadOnly(new ClassSpell[1]
		{
			new ClassSpell("hl_headsman", "The Headsman's Swing", "SwordMistwalker", "bring your greatsword down on one enemy in front of you, finishing it if it is already failing.", 0f, 18f, 45f, 1, SpellKind.Behead, "highlander", 0f, 40f)
		});

		public static IReadOnlyList<Talent> Highlander { get; } = TreeWeb.Build(HighlanderNodes, HighlanderLanes);

		public static IReadOnlyList<ClassSpell> RogueSpells { get; } = Array.AsReadOnly(new ClassSpell[1]
		{
			new ClassSpell("q_vanish", "Vanish", "CapeTrollHide", "break away: enemies within 30 metres lose track of you, and you move concealed and quiet for 6 seconds.", 0f, 45f, 0f, 1, SpellKind.Vanish, "rogue", 0f, 30f)
		});

		public static IReadOnlyList<ClassSpell> MonkSpells { get; } = Array.AsReadOnly(new ClassSpell[1]
		{
			new ClassSpell("n_clear", "Clear Mind", "FistFenrirClaw", "for 8 seconds your bare fists cost no stamina, strike 20% harder and stagger twice as heavily.", 0f, 30f, 0f, 1, SpellKind.Focus, "monk", 0f, 25f)
		});

		public static IReadOnlyList<ClassSpell> BerserkerSpells { get; } = Array.AsReadOnly(new ClassSpell[1]
		{
			new ClassSpell("b_whirl", "Whirlwind", "AxeIron", "spin with both axes, striking every visible enemy around you.", 0f, 15f, 30f, 5, SpellKind.Whirl, "berserker", 0f, 35f)
		});

		public static IReadOnlyList<Talent> Rogue { get; } = TreeWeb.Build(RogueNodes, RogueLanes);

		public static IReadOnlyList<Talent> Monk { get; } = TreeWeb.Build(MonkNodes, MonkLanes);

		public static float Value(Progression state, Bonus bonus)
		{
			return TreeWeb.Value((state.ClassId == "rogue") ? RogueAll : ((state.ClassId == "monk") ? MonkAll : ((state.ClassId == "berserker") ? BerserkerAll : ((state.ClassId == "highlander") ? HighlanderAll : ((state.ClassId == "breaker") ? BreakerAll : Array.Empty<TreeWeb.Node>())))), state, bonus);
		}
	}
	public static class BloodMoon
	{
		public const int DefaultInterval = 7;

		public const int MinimumInterval = 2;

		public const int MaximumInterval = 99;

		public const float MinimumSenses = 1f;

		public const float MaximumSenses = 3f;

		public const float DefaultSenses = 1.6f;

		public const float MinimumStars = 1f;

		public const float MaximumStars = 10f;

		public const float DefaultStars = 2.5f;

		public const double NightOffset = 0.15;

		public static double SleepLockSeconds => Tuning.F("BloodMoon/SleepLockSeconds", 90f, 0f, 3600f);

		public static int Interval(int interval)
		{
			if (interval >= 2)
			{
				if (interval <= 99)
				{
					return interval;
				}
				return 99;
			}
			return 7;
		}

		public static float Senses(float multiplier)
		{
			return Clamp(multiplier, 1f, 3f);
		}

		public static float Stars(float multiplier)
		{
			return Clamp(multiplier, 1f, 10f);
		}

		private static float Clamp(float value, float low, float high)
		{
			if (!float.IsNaN(value))
			{
				if (!(value < low))
				{
					if (!(value > high))
					{
						return value;
					}
					return high;
				}
				return low;
			}
			return low;
		}

		public static int NightIndex(double timeSeconds, double dayLengthSeconds)
		{
			if (dayLengthSeconds <= 0.0 || double.IsNaN(timeSeconds) || double.IsInfinity(timeSeconds) || double.IsNaN(dayLengthSeconds))
			{
				return 0;
			}
			double num = Math.Floor((timeSeconds - dayLengthSeconds * 0.15) / dayLengthSeconds);
			if (!(num < -2147483648.0))
			{
				if (!(num > 2147483647.0))
				{
					return (int)num;
				}
				return int.MaxValue;
			}
			return int.MinValue;
		}

		public static bool IsNightFraction(double fraction)
		{
			if (double.IsNaN(fraction) || double.IsInfinity(fraction))
			{
				return false;
			}
			fraction -= Math.Floor(fraction);
			if (!(fraction < 0.15))
			{
				return fraction >= 0.85;
			}
			return true;
		}

		public static bool IsNight(double timeSeconds, double dayLengthSeconds)
		{
			if (dayLengthSeconds <= 0.0 || double.IsNaN(dayLengthSeconds) || double.IsInfinity(dayLengthSeconds) || double.IsNaN(timeSeconds) || double.IsInfinity(timeSeconds))
			{
				return false;
			}
			return IsNightFraction(timeSeconds / dayLengthSeconds);
		}

		public static bool SleepLocked(double timeSeconds, double dayLengthSeconds)
		{
			double num = SecondsSinceMoonrise(timeSeconds, dayLengthSeconds);
			if (num >= 0.0)
			{
				return num < SleepLockSeconds;
			}
			return false;
		}

		public static double SecondsSinceMoonrise(double timeSeconds, double dayLengthSeconds)
		{
			if (dayLengthSeconds <= 0.0 || double.IsNaN(dayLengthSeconds) || double.IsInfinity(dayLengthSeconds) || double.IsNaN(timeSeconds) || double.IsInfinity(timeSeconds))
			{
				return -1.0;
			}
			double num = timeSeconds / dayLengthSeconds;
			num -= Math.Floor(num);
			if (double.IsNaN(num))
			{
				return -1.0;
			}
			if (num >= 0.85)
			{
				return (num - 0.85) * dayLengthSeconds;
			}
			if (num < 0.15)
			{
				return (num + 0.15) * dayLengthSeconds;
			}
			return -1.0;
		}

		public static bool IsBlood(int nightIndex, int interval)
		{
			if (nightIndex > 0)
			{
				return nightIndex % Interval(interval) == 0;
			}
			return false;
		}

		public static int NightsUntil(int nightIndex, int interval)
		{
			int num = Interval(interval);
			int num2 = nightIndex % num;
			if (num2 != 0 || nightIndex <= 0)
			{
				return num - num2;
			}
			return 0;
		}
	}
	public sealed class ClassDefinition
	{
		public string Id { get; }

		public string Name { get; }

		public string Description { get; }

		public IReadOnlyList<string> Branches { get; }

		public IReadOnlyList<Talent> Talents { get; }

		public string UnlockRequirement { get; }

		public string Icon { get; }

		public ClassDefinition(string id, string name, string description, string[] branches, IReadOnlyList<Talent> talents, string unlockRequirement = "", string icon = "")
		{
			Id = id;
			Name = name;
			Description = description;
			Branches = Array.AsReadOnly((branches.Length == 0) ? branches : branches.Concat(new string[1] { "DEFENSE" }).ToArray());
			Talents = ((branches.Length == 0) ? talents : DefenseTree.Append(id, talents, branches.Length));
			UnlockRequirement = unlockRequirement;
			Icon = ((icon.Length > 0) ? icon : (talents.FirstOrDefault()?.Icon ?? ""));
		}

		public Talent? Find(string id)
		{
			return Talents.FirstOrDefault((Talent t) => t.Id == id);
		}
	}
	public static class Classes
	{
		public static IReadOnlyList<ClassDefinition> All { get; } = Array.AsReadOnly(new ClassDefinition[13]
		{
			new ClassDefinition("huscarl", "Huscarl", "A sworn household warrior. Master your weapons, punish openings and carry the fight forward.", new string[6] { "COUNTER", "BREAKER", "ADVANCE", "FOOTWORK", "COMPOSURE", "MASTERY" }, VikingTrees.Huscarl, "", "SwordIron"),
			new ClassDefinition("shieldbearer", "Shieldbearer", "Stand in the shield wall. Turn successful shield blocks into counters and protection for nearby allies. No bows.", new string[6] { "WALL", "COUNTER", "RALLY", "REBUKE", "RESOLVE", "SHELTER" }, VikingTrees.Shieldbearer, "", "ShieldBanded"),
			new ClassDefinition("valkyrie", "Valkyrie", "Rise above the battle. Strike from the air, reach the wounded and shield those who still fight. No bows.", new string[6] { "WINGS", "SKYSTRIKE", "DESCENT", "MERCY", "SPEAR", "WINDWARD" }, VikingTrees.Valkyrie, "", "SpearWolfFang"),
			new ClassDefinition("ranger", "Ranger", "Keep your distance. Place your shots. Find a path through the wilds. No metal armour.", new string[6] { "PRECISION", "BARBS", "IMPACT", "SKIRMISH", "HUNT", "TRAIL" }, RangerTree.Talents),
			new ClassDefinition("mage", "Mage", "Build your Eitr reserve. Unlock elemental spells at the ends of your branches. No metal armour.", new string[6] { "EMBER", "FROST", "STORM", "CINDERS", "RIME", "TEMPEST" }, MageTree.Talents, "", "StaffFireball"),
			new ClassDefinition("warlock", "Warlock", "Offer blood for protection. Bind the dead, drain enemies and weave curses. No metal armour.", new string[6] { "BLOOD", "BONE", "GRAVE", "HUNGER", "COVEN", "HEX" }, WarlockTree.Talents, "", "StaffShield"),
			new ClassDefinition("rogue", "Rogue", "Stealth, opening strikes and evasive footwork. One-handed weapons, paired knives and axes, and the bow family, with the pickaxe kept for mining. No shields or metal armour.", new string[6] { "AMBUSH", "WOUNDS", "COUNTER", "SHADOW", "ESCAPE", "LANDING" }, MartialTrees.Rogue, "", "KnifeFlint"),
			new ClassDefinition("monk", "Monk", "Fight with bare fists or a claw, and gather bare-handed. Build combinations and turn defence into openings. No other weapons, shields or metal armour.", new string[6] { "COMBINATION", "PALM", "FLOW", "CALM", "SWEEP", "GUARD" }, MartialTrees.Monk, "", "FistFenrirClaw"),
			new ClassDefinition("hunter", "Hunter", "Fight alongside a permanent wolf companion. Call it back after death; strengthen your bond as you grow.", new string[6] { "FANGS", "PACK", "PURSUIT", "VIGOR", "CARE", "KINSHIP" }, HunterTree.Talents),
			new ClassDefinition("berserker", "Berserker", "Fight with paired axes, and carry a single axe or pickaxe to gather. Build brutal combinations and turn evasions into counterattacks.", new string[6] { "FRENZY", "REND", "IMPACT", "COUNTER", "BLOOD", "CLEAVE" }, MartialTrees.Berserker, "Equip a pair of dual-wielded axes to unlock.", "AxeEarly"),
			new ClassDefinition("highlander", "Highlander", "Fight with two-handed swords, and carry a single axe or pickaxe to gather. An executioner: wait for the swing, mark what is dying, and finish it outright.", new string[6] { "EXECUTION", "HEFT", "MORTAL", "BREAK", "RESOLVE", "HEADSMAN" }, MartialTrees.Highlander, "Equip a two-handed sword to unlock.", "THSwordKrom"),
			new ClassDefinition("breaker", "Breaker", "Fight with two-handed axes, and carry a pickaxe to mine. A breaker: crack an enemy open blow by blow, then spend the damage the moment it loses its footing.", new string[6] { "FRACTURE", "SHATTER", "QUAKE", "ANVIL", "ENDURE", "SUNDER" }, MartialTrees.Breaker, "Equip a two-handed axe to unlock.", "Battleaxe"),
			new ClassDefinition("dragonsworn", "Dragonsworn", "Fight alongside a young dragon of Moder's brood. It climbs while you wait and you spend the height on one stoop. No metal armour.", new string[6] { "SCALES", "RIME", "ALTITUDE", "STOOP", "BROOD", "KINSHIP" }, DragonTree.Talents, "Carry a dragon tear to unlock.", "TrophyDragonQueen")
		});

		public static ClassDefinition? Find(string id)
		{
			return All.FirstOrDefault((ClassDefinition c) => c.Id == id);
		}
	}
	public static class ClassResources
	{
		public static float Health(Progression state)
		{
			if (state.Definition != null)
			{
				return (Tuning.F("Resources." + state.ClassId + "/BaseHealth", 0f, 0f, 10000f) + state.Definition.Talents.Where((Talent t) => state.Has(t.Id)).Sum((Talent t) => t.MaximumHealth)) * Tuning.F("Resources." + state.ClassId + "/HealthMultiplier", 1f, 0f, 10f);
			}
			return 0f;
		}

		public static float Stamina(Progression state)
		{
			if (state.Definition != null)
			{
				return (Tuning.F("Resources." + state.ClassId + "/BaseStamina", 0f, 0f, 10000f) + state.Definition.Talents.Where((Talent t) => state.Has(t.Id)).Sum((Talent t) => t.MaximumStamina)) * Tuning.F("Resources." + state.ClassId + "/StaminaMultiplier", 1f, 0f, 10f);
			}
			return 0f;
		}
	}
	public sealed class ConfigurationLayout
	{
		private static readonly Dictionary<string, ConfigurationLayout> Cache = new Dictionary<string, ConfigurationLayout>();

		public string Group { get; }

		public string Section { get; }

		public string Key { get; }

		public string Description { get; }

		public string PreviousAddress { get; }

		public bool Extra => Section.StartsWith("ZZ - Extras - ", StringComparison.Ordinal);

		public string Address => Section + "/" + Key;

		public static IReadOnlyList<string> Groups { get; } = new string[6] { "Progression", "Shared combat", "Classes", "World events", "Local preferences", "Extras" };

		private ConfigurationLayout(string group, string section, string key, string description, string previousAddress)
		{
			Group = group;
			Section = section;
			Key = key;
			Description = description;
			PreviousAddress = previousAddress;
		}

		public static string GroupSummary(string group)
		{
			return group switch
			{
				"Progression" => "Boss EXP rewards and death penalties; broad EXP controls remain in the same section.", 
				"Shared combat" => "Ordinary ability and spell damage gained per level.", 
				"Classes" => "One section per class for resource bonuses, base spell values and companion stats.", 
				"World events" => "Raid availability, warning/wave/rest times and rewards.", 
				"Local preferences" => "Talent-tree zoom and ability visuals, chosen separately by each player.", 
				"Extras" => "Advanced talent values, proc timing, defensive formulas, equipment exceptions and raid internals. Grouped by class or system at the bottom of the file.", 
				_ => throw new ArgumentException("Unknown configuration group: " + group), 
			};
		}

		public static ConfigurationLayout For(TuningParameter p)
		{
			if (Cache.TryGetValue(p.Path, out ConfigurationLayout value))
			{
				return value;
			}
			string text = p.Section;
			string text2 = Words(p.Key);
			int num = text.IndexOf('.');
			string category = ((num < 0) ? text : text.Substring(0, num));
			string owner = ((num < 0) ? "" : text.Substring(num + 1));
			string text3;
			if (category == "Talents" || category == "SpellUpgrades" || category == "TalentEffects")
			{
				ClassDefinition classDefinition = Classes.All.FirstOrDefault((ClassDefinition c) => c.Talents.Any((Talent t) => t.Id == owner));
				Talent talent = ((classDefinition != null) ? classDefinition.Talents.Single((Talent t) => t.Id == owner) : WarriorTree.Find(owner));
				if (talent == null)
				{
					throw new InvalidOperationException("No class for tuning entry: " + p.Path);
				}
				string name = talent.Name;
				if (classDefinition != null && classDefinition.Talents.Count((Talent t) => t.Name == name) > 1)
				{
					name = name + " (" + owner + ")";
				}
				text = "Class - " + (classDefinition?.Name ?? "Legacy Warrior") + " - " + ((category == "Talents") ? "Talents" : "Talent effects");
				text2 = name + " - " + ((category == "Talents" && p.Key == "Amount") ? "Bonus amount" : Words(p.Key));
				if (category == "SpellUpgrades")
				{
					text2 += " - Spell upgrade";
				}
				text3 = "Classes";
			}
			else if (category == "Spells")
			{
				ClassSpell classSpell = ClassMagic.Find(owner) ?? throw new InvalidOperationException("Unknown spell: " + owner);
				text = "Class - " + Classes.Find(classSpell.ClassId).Name + " - Spells";
				text2 = classSpell.Name + " - " + Words(p.Key);
				text3 = "Classes";
			}
			else if (category == "Raids")
			{
				SiegeWarband siegeWarband = SiegeWarbands.All.Single((SiegeWarband b) => b.Id == owner);
				text = "Raid - " + siegeWarband.Name;
				text3 = "World events";
				if (p.Key.StartsWith("Count.", StringComparison.Ordinal))
				{
					text2 = "Troop count - " + Words(p.Key.Substring(6));
				}
			}
			else if (num >= 0)
			{
				string text4 = owner.ToLowerInvariant();
				if (text4 == "dragon")
				{
					text4 = "dragonsworn";
				}
				ClassDefinition classDefinition2 = Classes.Find(text4) ?? throw new InvalidOperationException("Unknown class section: " + text);
				text = "Class - " + classDefinition2.Name + " - " + category;
				text3 = "Classes";
				if (category == "Abilities")
				{
					int num2 = p.Key.IndexOf('.');
					if (num2 >= 0)
					{
						string variant = p.Key.Substring(num2 + 1);
						Talent talent2 = classDefinition2.Talents.FirstOrDefault((Talent t) => t.Id == variant);
						string text5 = ((talent2 != null) ? talent2.Name : (variant.StartsWith("Base", StringComparison.Ordinal) ? ("Default" + variant.Substring(4)) : Words(variant)));
						text2 = Words(p.Key.Substring(0, num2)) + " - " + text5;
					}
				}
			}
			else
			{
				ClassDefinition classDefinition3 = Classes.All.FirstOrDefault((ClassDefinition c) => string.Equals(c.Id, category, StringComparison.OrdinalIgnoreCase));
				if (classDefinition3 != null)
				{
					text = "Class - " + classDefinition3.Name + " - Mechanics";
					text3 = "Classes";
				}
				else
				{
					switch (category)
					{
					case "Progression":
						text3 = "Progression";
						break;
					case "Casting":
					case "Scaling":
					case "Defense":
					case "Martial":
						text = "Combat - " + category;
						text3 = "Shared combat";
						break;
					case "BloodMoon":
					case "Sieges":
						text3 = "World events";
						break;
					case "Effects":
					case "Interface":
						text3 = "Local preferences";
						break;
					default:
						throw new InvalidOperationException("Ungrouped config section: " + text);
					}
				}
				if (category == "Progression" || category == "Sieges" || category == "BloodMoon" || p.Local)
				{
					text2 = p.Key;
				}
			}
			text = ConfigName(text);
			text2 = ConfigName(text2);
			string previousAddress = text + "/" + text2;
			string text7;
			string text8;
			int num4;
			string text9;
			if (text3 == "Classes")
			{
				string text6 = text.Substring("Class - ".Length);
				int num3 = text6.IndexOf(" - ", StringComparison.Ordinal);
				text7 = text6.Substring(0, num3);
				text8 = text6.Substring(num3 + 3);
				if (!(category == "Resources") && !(category == "Spells"))
				{
					if (!(category == "Companions"))
					{
						num4 = 0;
						goto IL_0730;
					}
					if (!(p.Key == "BaseHealth") && !(p.Key == "HealthPerLevel") && !(p.Key == "BaseDamage"))
					{
						num4 = ((p.Key == "DamagePerLevel") ? 1 : 0);
						if (num4 == 0)
						{
							goto IL_0730;
						}
					}
					else
					{
						num4 = 1;
					}
				}
				else
				{
					num4 = 1;
				}
				text9 = "Tuning - " + text7;
				goto IL_074a;
			}
			if (category == "Raids")
			{
				text2 = text + " - " + text2;
				if (p.Key == "Enabled")
				{
					text = "Sieges";
				}
				else
				{
					text = "ZZ - Extras - World events";
					text3 = "Extras";
				}
			}
			else if (category == "Progression" && p.Key != "DeathLossFraction" && p.Key != "BossExperienceMultiplier")
			{
				text = "ZZ - Extras - Progression";
				text3 = "Extras";
			}
			else if (category == "Sieges" && !new string[8] { "WarningSeconds", "FirstWaveSeconds", "SecondWaveSeconds", "FirstRaidDelay", "PortalFirstRaidDelay", "RaidRestSeconds", "RewardCoinsBase", "RewardCoinsPerTier" }.Contains(p.Key))
			{
				text = "ZZ - Extras - World events";
				text2 = "Sieges - " + text2;
				text3 = "Extras";
			}
			else if (category == "BloodMoon")
			{
				text = "ZZ - Extras - World events";
				text2 = "Blood Moon - " + text2;
				text3 = "Extras";
			}
			else if (text3 == "Shared combat")
			{
				if (category == "Scaling")
				{
					text = "Combat";
				}
				else
				{
					text = "ZZ - Extras - Combat";
					text2 = category + " - " + text2;
					text3 = "Extras";
				}
			}
			goto IL_08f4;
			IL_08f4:
			string text10 = Regex.Replace(p.Description, " Source: [^\\r\\n]+$", "");
			string text11 = Regex.Replace(p.Path.Replace("/", ": ").Replace(".", " "), "(?<=[a-z])(?=[A-Z])", " ") + ".";
			if (text10.StartsWith(text11, StringComparison.Ordinal))
			{
				text10 = text10.Substring(text11.Length).TrimStart();
			}
			text10 = text10.Replace("Base is the normal value; a talent ID selects its learned variant. Additive variants add to Base.", "Default is the ordinary value; a named talent selects its learned variant. Additive variants add to Default.");
			text10 = p.Path switch
			{
				"Progression/MinimumKillExperience" => "Minimum EXP for an eligible kill, before boss and global multipliers.", 
				"Progression/MaximumKillExperience" => "Maximum health-based EXP for a kill, before boss and global multipliers.", 
				"Progression/HealthExperienceFactor" => "Multiply the square root of enemy maximum HP by this value to calculate health-based EXP.", 
				"Progression/BossExperienceMultiplier" => "Multiply a boss's health-based EXP reward by this value.", 
				"Progression/AttackDamagePerExperience" => "Each full amount of enemy attack damage adds 1 EXP, subject to the attack bonus caps.", 
				"Progression/AttackExperienceCap" => "Maximum additional EXP from an enemy's strongest attack.", 
				"Progression/AttackExperienceFractionCap" => "Attack EXP cannot exceed this fraction of the health/boss reward. 0.15 means 15%.", 
				"Progression/DeathLossFraction" => "Fraction of progress toward the next level lost on death, rounded up. 0 disables the penalty; earned levels remain.", 
				"Casting/MaximumReleasesPerCast" => "Maximum projectile releases in one cast, including doubled volleys. A lower value also limits learned upgrades.", 
				"Casting/DefaultDuration" => "Default duration in seconds for class abilities without their own duration setting.", 
				"Casting/DefaultReach" => "Default reach in metres for class abilities without their own reach setting.", 
				"Scaling/AbilityDamagePerLevel" => "Additional ordinary ability damage per class level after level 1. 0.01 means 1% per level.", 
				"Scaling/SpellPowerPerLevel" => "Additional Mage/Warlock spell power per class level after level 1. 0.04 means 4% per level.", 
				"Interface/TreeMinimumZoom" => "Talent tree zoom when centered, and the smallest allowed zoom. 1 is the unscaled tree size.", 
				"Interface/TreeMaximumZoom" => "Largest talent tree zoom. Cannot be lower than TreeMinimumZoom.", 
				"Interface/TreeZoomStep" => "Zoom multiplier per mouse-wheel notch. Values closer to 1 give smaller steps.", 
				"Effects/AbilitySizeMultiplier" => "Size multiplier for effects created by the shared ability-effect helper. 1 preserves the normal size.", 
				"Effects/AbilityLightIntensity" => "Brightness multiplier for point lights added by abilities. 0 turns those lights off; 1 preserves normal brightness.", 
				_ => text10, 
			};
			if (category == "Casting" && p.Key.EndsWith("Recovery", StringComparison.Ordinal))
			{
				text10 = "Seconds before another spell or class ability can be cast, independently of the individual spell cooldown.";
			}
			ConfigurationLayout configurationLayout = new ConfigurationLayout(text3, text, text2, (text10.Length == 0) ? (Words(p.Key) + ".") : text10, previousAddress);
			Cache.Add(p.Path, configurationLayout);
			return configurationLayout;
			IL_074a:
			text = text9;
			text2 = text8 + " - " + text2;
			if (num4 == 0)
			{
				text3 = "Extras";
			}
			goto IL_08f4;
			IL_0730:
			text9 = "ZZ - Extras - " + text7;
			goto IL_074a;
		}

		private static string Words(string text)
		{
			text = Regex.Replace(text, "(?<=[a-z0-9])(?=[A-Z])", " ");
			text = Regex.Replace(text, "(?<=[A-Z])(?=[A-Z][a-z])", " ");
			return text.Replace(".", " - ").Replace("_", " ").Trim();
		}

		private static string ConfigName(string text)
		{
			return text.Replace("'", "").Replace("\"", "").Replace("[", "(")
				.Replace("]", ")")
				.Replace("=", "-")
				.Replace("\\", "-")
				.Replace("\r", " ")
				.Replace("\n", " ")
				.Replace("\t", " ");
		}
	}
	public static class DefenseTree
	{
		public const int NodeCount = 4;

		public static string Id(string classId, int rank)
		{
			return classId + "_defense_" + rank;
		}

		public static int Rank(Progression state)
		{
			for (int num = 4; num > 0; num--)
			{
				if (state.Has(Id(state.ClassId, num)))
				{
					return num;
				}
			}
			return 0;
		}

		public static IReadOnlyList<Talent> Append(string classId, IReadOnlyList<Talent> original, int branch)
		{
			List<Talent> list = original.ToList();
			string prerequisite = original.Single((Talent t) => t.Prerequisite == null).Id;
			for (int num = 1; num <= 4; num++)
			{
				(string, string, string) tuple = Describe(classId, num);
				string text = Id(classId, num);
				list.Add(new Talent(text, tuple.Item1, tuple.Item2, prerequisite, tuple.Item3, branch, num, 0f, 0f, num == 4));
				prerequisite = text;
			}
			return list.AsReadOnly();
		}

		public static float EitrEfficiency(int rank)
		{
			if (rank != 0)
			{
				return Tuning.F("Defense.Mage/EitrEfficiencyBase", 1f, 0f, 100f) + (float)(rank - 1) * Tuning.F("Defense.Mage/EitrEfficiencyPerRank", 0.25f, 0f, 100f);
			}
			return 0f;
		}

		public static float PoolFraction(int rank, int level)
		{
			return (float)Math.Max(0, Math.Min(4, rank)) * Tuning.F("Defense/PoolFractionPerRank", 0.05f, 0f, 0.25f) * (Tuning.F("Defense/LevelOnePoolMultiplier", 0.5f, 0f, 1f) + (1f - Tuning.F("Defense/LevelOnePoolMultiplier", 0.5f, 0f, 1f)) * (float)(Math.Max(1, Math.Min(80, level)) - 1) / 79f);
		}

		public static float Capacity(Progression state, float maxHealth)
		{
			if (!float.IsNaN(maxHealth) && !float.IsInfinity(maxHealth))
			{
				return Math.Max(0f, maxHealth) * PoolFraction(Rank(state), state.Level);
			}
			return 0f;
		}

		public static float WolfShare(int rank)
		{
			if (rank != 0)
			{
				return Math.Min(1f, Tuning.F("Defense.Hunter/SharedDamageBase", 0.15f, 0f, 1f) + (float)rank * Tuning.F("Defense.Hunter/SharedDamagePerRank", 0.05f, 0f, 0.25f));
			}
			return 0f;
		}

		public static float CompanionRange(int rank)
		{
			if (rank != 0)
			{
				return Tuning.F("Defense/CompanionRangeBase", 8f, 0f, 100f) + (float)rank * Tuning.F("Defense/CompanionRangePerRank", 4f, 0f, 25f);
			}
			return 0f;
		}

		public static float Reduction(int rank)
		{
			return (float)rank * Tuning.F("Defense/DamageReductionPerRank", 0.05f, 0f, 0.125f);
		}

		public static string Description(Progression state, Talent talent)
		{
			return Description(state.ClassId, state.Level, talent);
		}

		public static string Description(string classId, int level, Talent talent)
		{
			if (talent.Branch != 6 || !talent.Id.StartsWith(classId + "_defense_", StringComparison.Ordinal))
			{
				return talent.Description;
			}
			return (Tuning.Customized ? "Server balance is customized; timings may differ.\n" : "") + Describe(classId, talent.Tier, level).description;
		}

		private static (string name, string description, string icon) Describe(string id, int rank, int level = 80)
		{
			string text = ((rank == 1) ? "" : (" " + (new string[5] { "", "", "II", "III", "IV" })[rank]));
			float num = Reduction(rank) * 100f;
			string text2 = $"{PoolFraction(rank, level) * 100f:0.##}% max HP";
			string text3;
			string item;
			string item2;
			switch (id)
			{
			case "mage":
				text3 = "Eitr Shield";
				item = "Eitr";
				item2 = $"Damage spends Eitr before health, after armour: 1 Eitr absorbs {EitrEfficiency(rank):0.##} damage. Excess reaches health. Spells use the same reserve.";
				break;
			case "warlock":
				text3 = "Self-Ward";
				item = "StaffShield";
				item2 = "Ward: " + text2 + ", scaling with class level. Reforms after 12s without damage; starts empty. Excess hurts. Blood costs bypass it.";
				break;
			case "hunter":
				text3 = "Shared Burden";
				item = "TrophyWolf";
				item2 = $"Your living bonded wolf within {CompanionRange(rank):0}m takes {WolfShare(rank) * 100f:0}% of damage after your armour. The share bypasses wolf armour and can kill it. No wolf means full damage.";
				break;
			case "huscarl":
				text3 = "Weapon Guard";
				item = "SwordIron";
				item2 = $"Weapon blocks bank half the damage stopped, up to {text2}, for 6s. Cap scales with class level. Blocking also takes {num:0}% less damage.";
				break;
			case "shieldbearer":
				text3 = "Living Bulwark";
				item = "ShieldBanded";
				item2 = $"Block wards you and 2 allies within 8m for {text2} each, 6s, every 8s. Scales with your level. Shield: {num:0}% less damage, doubled blocking.";
				break;
			case "valkyrie":
				text3 = "Sky Mantle";
				item = "CapeFeather";
				item2 = $"After 1s airborne, gain {text2} guard until 4s after landing. Every 10s; scales with class level. Air/fall damage reduced {num:0}%.";
				break;
			case "ranger":
				text3 = "Evasive Stride";
				item = "CapeDeerHide";
				item2 = $"Walk 6m to guard the next hit for {text2}, scaling with class level. One charge. Dodging also reduces damage {num:0}% for 4s.";
				break;
			case "rogue":
				text3 = "Fade Away";
				item = "KnifeFlint";
				item2 = $"Dodge guards the next hit for {text2} within 2s, every 8s. Scales with class level. Dodging also reduces damage {num * 2f:0}% for 2s.";
				break;
			case "monk":
				text3 = "Flow Guard";
				item = "FistFenrirClaw";
				item2 = "Fist/claw hits guard the next hit for " + text2 + " within 3s, every 4s. Scales with class level. Requires fists/claws; spends no stamina.";
				break;
			case "berserker":
				text3 = "Blood Hide";
				item = "AxeEarly";
				item2 = $"Paired-axe hits bank 20% of damage, up to {text2}, for 5s. Cap scales with class level. At half health, take {num * 2f:0}% less damage.";
				break;
			case "highlander":
				text3 = "Iron Commitment";
				item = "THSwordKrom";
				item2 = $"Stand still 1.5s with a greatsword: guard one hit during your next swing for {text2}, scaling with class level. Attacks take {num * 2f:0}% less damage.";
				break;
			case "breaker":
				text3 = "Anvil Guard";
				item = "Battleaxe";
				item2 = $"Wielding a battleaxe:\nAlways take {num:0}% less damage ({num * 2f:0}% while you are staggered).\nDamaging hits leaving foes staggered grant or refresh {rank + 2}s stagger immunity.";
				break;
			case "dragonsworn":
				text3 = "Brood Shelter";
				item = "TrophyDragonQueen";
				item2 = $"Whelp within {CompanionRange(rank):0}m: {num:0}% less damage. Spend 25% altitude to absorb {text2}, every 3s. Absorption scales with class level.";
				break;
			default:
				throw new ArgumentException("Defense branch missing for " + id);
			}
			return (name: text3 + ((rank == 4) ? "" : text), description: item2, icon: item);
		}
	}
	public readonly struct DefenseContext
	{
		public readonly bool Blocking;

		public readonly bool Shield;

		public readonly bool Airborne;

		public readonly bool Attacking;

		public readonly bool Staggering;

		public readonly bool WeaponReady;

		public readonly bool CompanionNear;

		public readonly float HealthFraction;

		public readonly float Eitr;

		public readonly float Stamina;

		public readonly float Altitude;

		public DefenseContext(float healthFraction = 1f, float eitr = 0f, float stamina = 0f, bool blocking = false, bool shield = false, bool airborne = false, bool attacking = false, bool staggering = false, bool weaponReady = true, bool companionNear = false, float altitude = 0f)
		{
			HealthFraction = healthFraction;
			Eitr = eitr;
			Stamina = stamina;
			Altitude = altitude;
			Blocking = blocking;
			Shield = shield;
			Airborne = airborne;
			Attacking = attacking;
			Staggering = staggering;
			WeaponReady = weaponReady;
			CompanionNear = companionNear;
		}
	}
	public readonly struct DefenseResult
	{
		public readonly float Damage;

		public readonly float EitrSpent;

		public readonly float StaminaSpent;

		public readonly float Shared;

		public readonly float AltitudeSpent;

		public DefenseResult(float damage, float eitr = 0f, float stamina = 0f, float shared = 0f, float altitude = 0f)
		{
			Damage = damage;
			EitrSpent = eitr;
			StaminaSpent = stamina;
			Shared = shared;
			AltitudeSpent = altitude;
		}
	}
	public sealed class DefenseCombat
	{
		private float _maxHealth;

		private string _class = "";

		private int _rank;

		private float _ward;

		private float _wardReadyAt;

		private float _dodgeUntil;

		private float _guard;

		private float _guardUntil;

		private float _travel;

		private float _airSince = -1f;

		private float _steadySince = -1f;

		private float _nextCover;

		private float _nextAfterimage;

		private float _nextWings;

		private float _nextStrike;

		private float _nextIntercept;

		private float _anchorUntil;

		private bool _airborne;

		private bool _wingCharged;

		private bool _swinging;

		public float WardRemaining => _ward;

		public float GuardRemaining => _guard;

		public float TravelProgress => _travel;

		public DefenseCombat(float maxHealth = 0f)
		{
			SetMaximumHealth(maxHealth);
		}

		public void SetMaximumHealth(float maxHealth)
		{
			float num = ((float.IsNaN(maxHealth) || float.IsInfinity(maxHealth)) ? 0f : Math.Max(0f, maxHealth));
			if (num != _maxHealth)
			{
				float num2 = ((_maxHealth > 0f) ? (num / _maxHealth) : 0f);
				_ward *= num2;
				_guard *= num2;
				_maxHealth = num;
			}
		}

		public float WardDelay(float now)
		{
			if (!(_class == "warlock") || _rank <= 0)
			{
				return 0f;
			}
			return Math.Max(0f, _wardReadyAt - now);
		}

		public void Clear()
		{
			_class = "";
			_rank = 0;
			_ward = 0f;
			_wardReadyAt = 0f;
			_dodgeUntil = 0f;
			_guard = 0f;
			_guardUntil = 0f;
			_travel = 0f;
			_airSince = -1f;
			_steadySince = -1f;
			_nextCover = (_nextAfterimage = (_nextWings = (_nextStrike = (_nextIntercept = (_anchorUntil = 0f)))));
			_airborne = (_wingCharged = (_swinging = false));
		}

		public void Tick(Progression state, float now)
		{
			int num = DefenseTree.Rank(state);
			if (_class != state.ClassId || _rank != num)
			{
				float ward = ((_class == "warlock" && state.ClassId == "warlock" && _rank > 0 && num > 0) ? Math.Min(_ward, DefenseTree.Capacity(state, _maxHealth)) : 0f);
				Clear();
				_ward = ward;
				_wardReadyAt = now + Tuning.F("Defense.Warlock/RechargeSeconds", 12f, 0.05f, 3600f);
				_class = state.ClassId;
				_rank = num;
			}
			if (_class == "warlock" && _rank > 0 && now >= _wardReadyAt)
			{
				_ward = DefenseTree.Capacity(state, _maxHealth);
			}
			_guard = Math.Min(_guard, DefenseTree.Capacity(state, _maxHealth));
			_ward = Math.Min(_ward, DefenseTree.Capacity(state, _maxHealth));
			if (now >= _guardUntil)
			{
				_guard = 0f;
			}
		}

		public void Dodge(Progression state, float now)
		{
			Tick(state, now);
			_dodgeUntil = now + ((state.ClassId == "ranger") ? Tuning.F("Defense.Ranger/DodgeProtectionSeconds", 4f, 0f, 60f) : ((state.ClassId == "rogue") ? Tuning.F("Defense.Rogue/DodgeProtectionSeconds", 2f, 0f, 60f) : 0f));
			if (_class == "rogue" && _rank > 0 && now >= _nextAfterimage)
			{
				_guard = DefenseTree.Capacity(state, _maxHealth);
				_guardUntil = now + Tuning.F("Defense.Rogue/AfterimageSeconds", 2f, 0.05f, 60f);
				_nextAfterimage = now + Tuning.F("Defense.Rogue/AfterimageCooldown", 8f, 0.05f, 3600f);
			}
		}

		public float Block(Progression state, float now, float stopped, bool weaponReady, bool shield)
		{
			Tick(state, now);
			if (_rank == 0 || stopped <= 0f || float.IsNaN(stopped) || float.IsInfinity(stopped))
			{
				return 0f;
			}
			if (_class == "huscarl" && weaponReady)
			{
				_guard = Math.Min(DefenseTree.Capacity(state, _maxHealth), _guard + stopped * Tuning.F("Defense.Huscarl/BlockedDamageStored", 0.5f, 0f, 1f));
				_guardUntil = now + Tuning.F("Defense.Huscarl/GuardSeconds", 6f, 0.05f, 3600f);
			}
			if (_class == "shieldbearer" && shield && now >= _nextCover)
			{
				_nextCover = now + Tuning.F("Defense.Shieldbearer/ShelterCooldown", 8f, 0.05f, 3600f);
				return DefenseTree.PoolFraction(_rank, state.Level);
			}
			return 0f;
		}

		public void Travel(Progression state, float now, float metres)
		{
			Tick(state, now);
			if (!(_class != "ranger") && _rank != 0 && !(_guard > 0f) && !(metres <= 0f) && !(metres > 2f) && !float.IsNaN(metres))
			{
				_travel += metres;
				if (_travel >= Tuning.F("Defense.Ranger/TravelMetres", 6f, 0.1f, 100f))
				{
					_travel = 0f;
					_guard = DefenseTree.Capacity(state, _maxHealth);
					_guardUntil = float.PositiveInfinity;
				}
			}
		}

		public void Air(Progression state, float now, bool airborne)
		{
			Tick(state, now);
			if (!(_class != "valkyrie") && _rank != 0)
			{
				if (airborne && !_airborne)
				{
					_airSince = now;
					_wingCharged = false;
				}
				if (airborne && !_wingCharged && _airSince >= 0f && now - _airSince >= Tuning.F("Defense.Valkyrie/AirborneChargeSeconds", 1f, 0.05f, 60f) && now >= _nextWings)
				{
					_guard = DefenseTree.Capacity(state, _maxHealth);
					_guardUntil = float.PositiveInfinity;
					_nextWings = now + Tuning.F("Defense.Valkyrie/MantleCooldown", 10f, 0.05f, 3600f);
					_wingCharged = true;
				}
				if (!airborne && _airborne && _guard > 0f)
				{
					_guardUntil = now + Tuning.F("Defense.Valkyrie/LandingRetentionSeconds", 4f, 0f, 60f);
				}
				_airborne = airborne;
			}
		}

		public void Posture(Progression state, float now, bool still, bool attacking, bool weaponReady)
		{
			Tick(state, now);
			if (_class != "highlander" || _rank == 0)
			{
				return;
			}
			if (_swinging && !attacking)
			{
				_guard = 0f;
			}
			_swinging = attacking;
			if (!weaponReady || (!attacking && !still))
			{
				_guard = 0f;
				_steadySince = -1f;
				return;
			}
			if (attacking)
			{
				_steadySince = -1f;
				return;
			}
			if (_steadySince < 0f)
			{
				_steadySince = now;
			}
			if (now - _steadySince >= Tuning.F("Defense.Highlander/StandingChargeSeconds", 1.5f, 0.05f, 60f))
			{
				_guard = DefenseTree.Capacity(state, _maxHealth);
				_guardUntil = float.PositiveInfinity;
			}
		}

		public void Strike(Progression state, float now, float healthLost, bool enemyStaggered, bool weaponReady)
		{
			Tick(state, now);
			if (_rank != 0 && weaponReady && !(healthLost <= 0f) && !float.IsNaN(healthLost) && !float.IsInfinity(healthLost) && !(now < _nextStrike))
			{
				if (_class == "monk")
				{
					_guard = DefenseTree.Capacity(state, _maxHealth);
					_guardUntil = now + Tuning.F("Defense.Monk/GuardSeconds", 3f, 0.05f, 60f);
					_nextStrike = now + Tuning.F("Defense.Monk/GuardCooldown", 4f, 0.05f, 3600f);
				}
				if (_class == "berserker")
				{
					_guard = Math.Min(DefenseTree.Capacity(state, _maxHealth), _guard + healthLost * Tuning.F("Defense.Berserker/DamageStored", 0.2f, 0f, 1f));
					_guardUntil = now + Tuning.F("Defense.Berserker/GuardSeconds", 5f, 0.05f, 3600f);
					_nextStrike = now + Tuning.F("Defense/StrikeCooldown", 0.5f, 0.05f, 60f);
				}
				if (_class == "breaker" && enemyStaggered)
				{
					_anchorUntil = now + (float)_rank * Tuning.F("Defense.Breaker/AnchorSecondsPerRank", 1f, 0f, 60f) + Tuning.F("Defense.Breaker/AnchorBaseSeconds", 2f, 0f, 60f);
					_nextStrike = now + Tuning.F("Defense/StrikeCooldown", 0.5f, 0.05f, 60f);
				}
			}
		}

		public bool PreventStagger(Progression state, float now, bool weaponReady)
		{
			Tick(state, now);
			if (_class == "breaker" && _rank > 0 && weaponReady)
			{
				return now < _anchorUntil;
			}
			return false;
		}

		public string Status(Progression state, float now)
		{
			Tick(state, now);
			if (_rank == 0)
			{
				return "";
			}
			switch (_class)
			{
			case "huscarl":
				return $"Weapon guard: {_guard:0}";
			case "shieldbearer":
				if (!(now < _nextCover))
				{
					return "Shelter: block to ward allies";
				}
				return $"Shelter: {_nextCover - now:0.0}s";
			case "valkyrie":
				return $"Sky mantle: {_guard:0}";
			case "ranger":
				if (!(_guard > 0f))
				{
					return $"Trail cover: {_travel:0.0}/6m";
				}
				return $"Trail cover: {_guard:0}";
			case "rogue":
				if (!(_guard > 0f))
				{
					if (!(now < _nextAfterimage))
					{
						return "Afterimage: ready on dodge";
					}
					return $"Afterimage: {_nextAfterimage - now:0.0}s";
				}
				return $"Afterimage: {_guard:0}";
			case "monk":
				if (!(_guard > 0f))
				{
					if (!(now < _nextStrike))
					{
						return "Flow guard: land a fist/claw hit";
					}
					return $"Flow guard: {_nextStrike - now:0.0}s";
				}
				return $"Flow guard: {_guard:0}";
			case "berserker":
				return $"Blood hide: {_guard:0}";
			case "highlander":
				return $"Iron poise: {_guard:0}";
			case "breaker":
				if (!(now < _anchorUntil))
				{
					return "Unshakable: break an enemy";
				}
				return $"Unshakable: {_anchorUntil 

BepInEx/plugins/WarriorRpg/WarriorRpg.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using SoftReferenceableAssets;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;
using Valheim.SettingsGui;
using Warrior.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("WarriorRpg")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.22.1.0")]
[assembly: AssemblyInformationalVersion("0.22.1+36024a3abb676c88560741336bac1ca89fd65235")]
[assembly: AssemblyProduct("WarriorRpg")]
[assembly: AssemblyTitle("WarriorRpg")]
[assembly: AssemblyVersion("0.22.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Warrior.Mod
{
	[BepInPlugin("local.warrior.rpg", "Oathbound", "0.22.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal enum VfxKind
		{
			Warcry,
			WarcryStrike,
			StandFast,
			StandFastAlly,
			Vanish,
			Focus,
			Whirl,
			WhirlStrike,
			PierceLaunch,
			PierceStrike,
			PierceFlight,
			SpearConjure,
			SpearStrike,
			SpearFlight,
			HandSpear,
			HandSpearGone,
			HunterCall,
			HunterMend,
			Sunder,
			SunderStrike,
			DragonCall,
			DragonWarm,
			DragonBreath,
			DragonGale,
			DefenseReady,
			DefenseHit,
			DefenseBreak,
			Skystrike
		}

		private sealed class Vfx
		{
			private readonly EffectList _list;

			private readonly float _scale;

			internal bool Ready => Resolved(_list);

			internal Vfx(float scale, params string[] names)
			{
				_scale = scale;
				_list = EffectListOf(scale, names);
			}

			internal void Play(Vector3 position, Quaternion rotation, Player? owner = null)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				Create(position, rotation, owner);
			}

			private GameObject[] Create(Vector3 position, Quaternion rotation, Player? owner)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				GameObject[] array = _list.Create(position, rotation, (Transform)null, _scale, -1, ((Object)(object)owner != (Object)null) ? ((Character)owner).GetZDOID() : ZDOID.None);
				GameObject[] array2 = array;
				for (int i = 0; i < array2.Length; i++)
				{
					Shrink(array2[i], 0.5f * Tuning.F("Effects/AbilitySizeMultiplier", 1f, 0.1f, 2f));
				}
				return array;
			}

			internal void PlayLocal(Vector3 position, Quaternion rotation, Player? owner = null)
			{
				//IL_000d: 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)
				bool forceDisableInit = ZNetView.m_forceDisableInit;
				ZNetView.m_forceDisableInit = true;
				try
				{
					Play(position, rotation, owner);
				}
				finally
				{
					ZNetView.m_forceDisableInit = forceDisableInit;
				}
			}

			internal void PlayCompanionCall(Vector3 position, Quaternion rotation, Player? owner = null)
			{
				//IL_000d: 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)
				bool forceDisableInit = ZNetView.m_forceDisableInit;
				ZNetView.m_forceDisableInit = true;
				try
				{
					GameObject[] array = Create(position, rotation, owner);
					for (int i = 0; i < array.Length; i++)
					{
						AudioSource[] componentsInChildren = array[i].GetComponentsInChildren<AudioSource>(true);
						foreach (AudioSource obj in componentsInChildren)
						{
							obj.spatialBlend = 1f;
							obj.minDistance = 2f;
							obj.maxDistance = 30f;
							obj.rolloffMode = (AudioRolloffMode)1;
						}
					}
				}
				finally
				{
					ZNetView.m_forceDisableInit = forceDisableInit;
				}
			}
		}

		private sealed class AbilityGlow : MonoBehaviour
		{
			private Light _light;

			private float _until;

			private float _peak;

			private float _fade;

			internal void Configure(float seconds, float range, float intensity, Color color, float fade)
			{
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				_light = ((Component)this).gameObject.AddComponent<Light>();
				_light.type = (LightType)2;
				_light.color = color;
				_light.range = range;
				_light.shadows = (LightShadows)0;
				intensity *= Tuning.F("Effects/AbilityLightIntensity", 1f, 0f, 2f);
				_light.intensity = intensity;
				_peak = intensity;
				_fade = Mathf.Clamp((fade > 0f) ? fade : (seconds * 0.5f), 0.05f, Mathf.Max(0.05f, seconds));
				_until = Time.unscaledTime + seconds;
			}

			private void Update()
			{
				if ((Object)(object)_light == (Object)null)
				{
					Object.Destroy((Object)(object)((Component)this).gameObject);
					return;
				}
				float num = _until - Time.unscaledTime;
				if (num <= 0f)
				{
					Object.Destroy((Object)(object)((Component)this).gameObject);
				}
				else
				{
					_light.intensity = _peak * Mathf.Clamp01(num / _fade);
				}
			}
		}

		private struct VfxBeat
		{
			internal VfxKind Kind;

			internal Vector3 Position;

			internal Vector3 Forward;

			internal ZDOID Owner;

			internal ZDOID Target;

			internal string Item;

			internal float Amount;

			internal float Deadline;
		}

		private sealed class VfxSpend
		{
			internal float Window;

			internal int Count;
		}

		private sealed class LevelCommand : ConsoleCommand
		{
			public override string Name => "oathbound_level";

			public override string Help => "<set|add|remove> <levels> [player] - admin: change a player's active Oathbound class level (default: yourself)";

			public override List<string> CommandOptionList()
			{
				return AdminLevelOps.ToList();
			}

			public override void Run(string[] args)
			{
				if (args.Length < 2 || !AdminLevelOps.Contains(args[0].ToLowerInvariant()) || !int.TryParse(args[1], out var result))
				{
					AdminPrint("usage: oathbound_level " + ((ConsoleCommand)this).Help);
					return;
				}
				string text = args[0].ToLowerInvariant();
				bool num;
				if (!(text == "set"))
				{
					num = result < 1;
				}
				else
				{
					if (result < 1)
					{
						goto IL_0066;
					}
					num = result > 80;
				}
				if (!num)
				{
					Instance?.RequestAdminLevel(text, result, string.Join(" ", args.Skip(2)).Trim());
					return;
				}
				goto IL_0066;
				IL_0066:
				AdminPrint((text == "set") ? ("a level is 1 to " + 80 + ".") : "add and remove take a positive number of levels.");
			}
		}

		[HarmonyPatch(typeof(Player), "Load")]
		private static class EquipmentLoadPatch
		{
			private static void Prefix(Player __instance)
			{
				LoadingEquipment.Add(__instance);
			}

			private static void Finalizer(Player __instance)
			{
				LoadingEquipment.Remove(__instance);
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class BerserkerBloodPatch
		{
			private static void Prefix(Character __instance, out float __state)
			{
				__state = __instance.GetHealth();
			}

			private static void Postfix(Character __instance, HitData hit, float __state)
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Invalid comparison between Unknown and I4
				//IL_0064: Unknown result type (might be due to invalid IL or missing references)
				Character attacker = hit.GetAttacker();
				Player val = (Player)(object)((attacker is Player) ? attacker : null);
				if (!((Object)(object)val == (Object)null) && ((Component)__instance).GetComponent<ZNetView>().IsOwner() && !hit.m_ranged && (int)hit.m_skill == 7 && !(__state <= Mathf.Max(0f, __instance.GetHealth())) && EnemyHit((Character)(object)val, __instance))
				{
					((Component)val).GetComponent<ZNetView>().InvokeRPC("Oathbound_BerserkerHit_1", new object[1] { __instance.GetZDOID() });
				}
			}
		}

		[HarmonyPatch(typeof(Character), "Awake")]
		private static class BerserkerRpcPatch
		{
			private static void Postfix(Character __instance)
			{
				Player player = (Player)(object)((__instance is Player) ? __instance : null);
				if (player == null)
				{
					return;
				}
				ZNetView view = ((Component)player).GetComponent<ZNetView>();
				if ((Object)(object)view == (Object)null || !view.IsValid())
				{
					return;
				}
				view.Register<ZDOID>("Oathbound_BerserkerHit_1", (Action<long, ZDOID>)delegate(long sender, ZDOID id)
				{
					//IL_0011: Unknown result type (might be due to invalid IL or missing references)
					//IL_007e: Unknown result type (might be due to invalid IL or missing references)
					//IL_0089: Unknown result type (might be due to invalid IL or missing references)
					Progression val = MartialState((Character)(object)player);
					GameObject obj = ZNetScene.instance.FindInstance(id);
					Character val2 = ((obj != null) ? obj.GetComponent<Character>() : null);
					if (view.IsOwner() && !(((val != null) ? val.ClassId : null) != "berserker") && !((Object)(object)val2 == (Object)null) && ((Component)val2).GetComponent<ZNetView>().GetZDO().GetOwner() == sender && EnemyHit((Character)(object)player, val2) && !(Vector3.Distance(((Component)player).transform.position, ((Component)val2).transform.position) > 12f))
					{
						float num = Instance._martial.OnBloodHit(val, (double)Time.unscaledTime, ((Character)player).GetHealthPercentage());
						if (num > 0f)
						{
							((Character)player).Heal(num, false);
						}
					}
				});
			}
		}

		[HarmonyPatch(typeof(BaseAI), "CanSeeTarget", new Type[]
		{
			typeof(Transform),
			typeof(Vector3),
			typeof(float),
			typeof(float),
			typeof(bool),
			typeof(bool),
			typeof(Character)
		})]
		private static class BloodMoonSight
		{
			private static void Prefix(ref float viewRange, ref float viewAngle, Character target)
			{
				float num = (((Object)(object)Instance != (Object)null && Instance._bloodMoonActive && (Object)(object)target != (Object)null && target.IsPlayer()) ? Instance._bloodMoonSenseNow : 1f);
				if (!(num <= 1f))
				{
					viewRange *= num;
					viewAngle = Mathf.Min(180f, viewAngle * num);
				}
			}
		}

		[HarmonyPatch(typeof(Character), "GetNoiseRange")]
		private static class BloodMoonNoise
		{
			private static void Postfix(Character __instance, ref float __result)
			{
				if (!((Object)(object)Instance == (Object)null) && Instance._bloodMoonActive && !(__result <= 0f) && __instance.IsPlayer())
				{
					__result *= Instance._bloodMoonSenseNow;
				}
			}
		}

		[HarmonyPatch(typeof(SpawnSystem), "GetLevelUpChance", new Type[]
		{
			typeof(Vector3),
			typeof(float)
		})]
		private static class BloodMoonStars
		{
			private static void Postfix(ref float __result)
			{
				if (!((Object)(object)Instance == (Object)null) && Instance._bloodMoonActive && !(__result <= 0f))
				{
					__result = Mathf.Min(100f, __result * Instance._bloodMoonStarNow);
				}
			}
		}

		[HarmonyPatch(typeof(Bed), "CheckEnemies")]
		private static class BloodMoonSleep
		{
			private static bool Prefix(Player human, ref bool __result)
			{
				if (!BloodMoonSleepLocked())
				{
					return true;
				}
				__result = false;
				if ((Object)(object)human != (Object)null)
				{
					((Character)human).Message((MessageType)2, "You cannot sleep while the blood moon is still rising.", 0, (Sprite)null, false);
				}
				return false;
			}
		}

		private sealed class BurningCredit
		{
			internal readonly ZDOID Player;

			internal BurningCredit(ZDOID player)
			{
				//IL_0007: 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)
				Player = player;
			}
		}

		[HarmonyPatch]
		private static class BurningSourcePatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				yield return AccessTools.Method(typeof(SE_Burning), "AddFireDamage", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(SE_Burning), "AddSpiritDamage", (Type[])null, (Type[])null);
			}

			private static void Postfix(SE_Burning __instance, Character ___m_character, float __0, bool __result)
			{
				//IL_0064: Unknown result type (might be due to invalid IL or missing references)
				Plugin instance = Instance;
				if ((Object)(object)instance == (Object)null || !__result || __0 <= 0f)
				{
					return;
				}
				instance._burningSources.Remove(__instance);
				if (instance._experienceDamage.TryGetValue(___m_character, out DamageContext value) && value.Hit != null)
				{
					Player val = ExperienceOwner(value.Hit.GetAttacker());
					if (!((Object)(object)val == (Object)null))
					{
						instance._burningSources.Add(__instance, new BurningCredit(((Character)val).GetZDOID()));
						instance.RecordExperienceHit(___m_character, value.Hit);
					}
				}
			}
		}

		[HarmonyPatch(typeof(SE_Burning), "UpdateStatusEffect")]
		private static class BurningTickContextPatch
		{
			private static void Prefix(SE_Burning __instance, Character ___m_character, out SE_Burning? __state)
			{
				__state = null;
				if (!((Object)(object)Instance == (Object)null) && !((Object)(object)___m_character == (Object)null))
				{
					DamageContext value = Instance._experienceDamage.GetValue(___m_character, (Character _) => new DamageContext());
					__state = value.Burning;
					value.Burning = __instance;
				}
			}

			private static void Finalizer(Character ___m_character, SE_Burning? __state)
			{
				if ((Object)(object)Instance != (Object)null && (Object)(object)___m_character != (Object)null && Instance._experienceDamage.TryGetValue(___m_character, out DamageContext value))
				{
					value.Burning = __state;
				}
			}
		}

		private sealed class Bleed
		{
			internal Character Target;

			internal string ClassId = "";

			internal string Talent = "";

			internal float TickDamage;

			internal float NextTick;

			internal int Remaining = 3;

			internal bool Fire;
		}

		[HarmonyPatch(typeof(Character), "Jump")]
		private static class AirStepPatch
		{
			private static void Prefix(Character __instance, out bool __state)
			{
				__state = (Object)(object)__instance == (Object)(object)Player.m_localPlayer && !__instance.IsOnGround() && !__instance.IsSwimming() && !__instance.IsDead() && !__instance.IsEncumbered() && !__instance.InDodge() && !__instance.InAttack() && !__instance.IsKnockedBack() && !__instance.IsStaggering() && !__instance.IsTeleporting() && !__instance.InCutscene() && !BlocksInput && ((Object)(object)Instance == (Object)null || Time.unscaledTime >= Instance._spellCastingUntil) && !Object.op_Implicit((Object)(object)GrapplingPoint.m_localGrappler);
			}

			private static void Postfix(Character __instance, bool __state)
			{
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				//IL_0036: Unknown result type (might be due to invalid IL or missing references)
				//IL_0058: Unknown result type (might be due to invalid IL or missing references)
				Progression val = (__state ? ActiveState(__instance, "valkyrie") : null);
				if (val != null && Instance._vikings.TryAirStep(val))
				{
					Vector3 linearVelocity = ((Component)__instance).GetComponent<Rigidbody>().linearVelocity;
					linearVelocity.y = Mathf.Max(linearVelocity.y, __instance.m_jumpForce);
					Instance._airStepInProgress = true;
					try
					{
						__instance.ForceJump(linearVelocity, true);
					}
					finally
					{
						Instance._airStepInProgress = false;
					}
					if (Instance._vikings.Weightless(val))
					{
						Instance._weightlessUntil = Time.unscaledTime + 3f;
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnJump")]
		private static class AirStepCostPatch
		{
			private static bool Prefix(Player __instance)
			{
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					Plugin? instance = Instance;
					if (instance == null)
					{
						return true;
					}
					return !instance._airStepInProgress;
				}
				return true;
			}
		}

		private sealed class ClassRow
		{
			internal RectTransform Rect;

			internal Image Background;

			internal Image Icon;

			internal Image Accent;

			internal TMP_Text Name;

			internal TMP_Text Status;

			internal ClassDefinition? Definition;
		}

		[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
		private static class ClassResourceCapacityPatch
		{
			private static void Postfix(Player __instance, ref float hp, ref float stamina)
			{
				Progression val = (((Object)(object)Instance == (Object)null) ? null : ActiveState((Character)(object)__instance, Instance._state.ClassId));
				if (val != null)
				{
					hp += ClassResources.Health(val);
					stamina += ClassResources.Stamina(val);
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
		private static class BlockTalentPatch
		{
			private static void Prefix(HitData hit, out float __state)
			{
				__state = hit.GetTotalBlockableDamage();
			}

			private static void Postfix(Humanoid __instance, HitData hit, Character attacker, bool __result, float __state)
			{
				//IL_0067: Unknown result type (might be due to invalid IL or missing references)
				//IL_0086: Expected O, but got Unknown
				Progression val = ActiveState((Character)(object)__instance);
				if (val != null && __result && EnemyHit((Character)(object)__instance, attacker) && ((Character)__instance).HaveStamina(0f) && !((Character)__instance).IsStaggering() && !(hit.GetTotalBlockableDamage() >= __state))
				{
					float num = Instance._combat.OnBlock(val, (double)Time.unscaledTime);
					if (num > 0f)
					{
						((Character)__instance).AddStamina(num);
						Instance.ShieldPulse((Player)__instance);
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyAttack")]
		private static class MeleeTalentPatch
		{
			private static void Postfix(Character ___m_character, SkillType skill, ref HitData hitData)
			{
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				Progression val = ActiveState(___m_character);
				if (val != null && IsMelee(skill) && !hitData.m_ranged && !(hitData.GetTotalDamage() <= 0f))
				{
					Character val2 = (((Object)(object)hitData.m_hitCollider != (Object)null) ? ((Component)hitData.m_hitCollider).GetComponentInParent<Character>() : null);
					bool num = (Object)(object)val2 != (Object)null && val2.GetHealth() > 0f && EnemyHit(___m_character, val2);
					bool flag = Instance._combat.IsRiposteReady(val, (double)Time.unscaledTime);
					((DamageTypes)(ref hitData.m_damage)).Modify(Instance._combat.MeleeDamage(val, (double)Time.unscaledTime));
					if (num && Instance._combat.IsCritical(val, (double)Time.unscaledTime, (double)Random.value))
					{
						((DamageTypes)(ref hitData.m_damage)).Modify(1.5f);
						Instance.CriticalNotice();
					}
					if (num && flag && !val2.IsDodgeInvincible())
					{
						Instance.BeginBleed(val2, hitData.GetTotalDamage());
					}
					if (val.Has("leverage"))
					{
						HitData obj = hitData;
						obj.m_staggerMultiplier *= 1.1f;
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyRunStaminaDrain")]
		private static class RunTalentPatch
		{
			private static void Postfix(Character ___m_character, ref float drain)
			{
				Progression val = ActiveState(___m_character);
				if (val != null && drain > 0f)
				{
					drain *= Instance._combat.RunCost(val, (double)Time.unscaledTime);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyDodgeStaminaUsage")]
		private static class DodgeTalentPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				Progression? obj = ActiveState(___m_character);
				if (obj != null && obj.Has("footwork") && staminaUse > 0f)
				{
					staminaUse *= 0.95f;
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyJumpStaminaUsage")]
		private static class JumpTalentPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				Progression? obj = ActiveState(___m_character);
				if (obj != null && obj.Has("lightstep") && staminaUse > 0f)
				{
					staminaUse *= 0.9f;
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyStaminaRegen")]
		private static class StaminaRegenTalentPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaMultiplier)
			{
				Progression? obj = ActiveState(___m_character);
				if (obj != null && obj.Has("breath"))
				{
					staminaMultiplier *= 1.03f;
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyHealthRegen")]
		private static class HealthRegenTalentPatch
		{
			private static void Postfix(Character ___m_character, ref float regenMultiplier)
			{
				Progression? obj = ActiveState(___m_character);
				if (obj != null && obj.Has("recuperation"))
				{
					regenMultiplier *= 1.05f;
				}
			}
		}

		[HarmonyPatch(typeof(Character), "AddStaggerDamage")]
		private static class StaggerTalentPatch
		{
			private static void Prefix(Character __instance, ref float damage)
			{
				Progression val = ActiveState(__instance);
				if (val != null && damage > 0f)
				{
					damage *= Instance._combat.Stagger(val, (double)Time.unscaledTime);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "GetBodyArmor")]
		private static class ArmourTalentPatch
		{
			private static void Postfix(Player __instance, ref float __result)
			{
				Progression? obj = ActiveState((Character)(object)__instance);
				if (obj != null && obj.Has("ironhide"))
				{
					__result += 2f;
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "OnDamaged")]
		private static class GritTalentPatch
		{
			private static void Prefix(Character ___m_character, HitData hit, Character attacker)
			{
				Progression? obj = ActiveState(___m_character);
				if (obj != null && obj.Has("grit") && EnemyHit(___m_character, attacker))
				{
					hit.m_damage.m_blunt *= 0.98f;
					hit.m_damage.m_slash *= 0.98f;
					hit.m_damage.m_pierce *= 0.98f;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDamaged")]
		private static class WoundedTalentPatch
		{
			private static void Postfix(Player __instance, HitData hit)
			{
				Progression val = ActiveState((Character)(object)__instance);
				if (val != null && !(hit.GetTotalDamage() <= 0f) && EnemyHit((Character)(object)__instance, hit.GetAttacker()))
				{
					if (Instance._combat.OnWounded(val, (double)Time.unscaledTime, ((Character)__instance).GetHealthPercentage()))
					{
						((Character)__instance).Heal(8f, false);
						((Character)__instance).AddStamina(20f);
					}
					float num = Instance._combat.Retaliation(val, (double)Time.unscaledTime, hit.GetTotalDamage());
					if (num > 0f)
					{
						Instance.DealBonusHit(__instance, hit.GetAttacker(), num, bleed: false);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		private static class PlayerDeathPenaltyPatch
		{
			private static void Prefix(Player __instance)
			{
				try
				{
					Instance?.ApplyPlayerDeath(__instance);
				}
				catch (Exception ex)
				{
					Plugin? instance = Instance;
					if (instance != null)
					{
						((BaseUnityPlugin)instance).Logger.LogError((object)("Warrior death penalty failed: " + ex));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnRespawn")]
		private static class PlayerRespawnPenaltyPatch
		{
			private static void Postfix(Player __instance)
			{
				Instance?._penalizedDeaths.Remove(__instance);
			}
		}

		private sealed class DefenseStaggerStamp
		{
			public int Frame = -1;
		}

		[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
		private static class DefenseBlockPatch
		{
			private static void Prefix(HitData hit, out float __state)
			{
				__state = hit.GetTotalBlockableDamage();
			}

			private static void Postfix(Humanoid __instance, HitData hit, Character attacker, bool __result, float __state)
			{
				Plugin instance = Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return;
				}
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val == null)
				{
					return;
				}
				Progression val2 = ActiveState((Character)(object)val, instance._state.ClassId);
				float num = __state - hit.GetTotalBlockableDamage();
				if (val2 == null || !__result || num <= 0f || !EnemyHit((Character)(object)val, attacker) || !((Character)val).HaveStamina(0f) || ((Character)val).IsStaggering())
				{
					return;
				}
				instance._defense.SetMaximumHealth(((Character)val).GetMaxHealth());
				float num2 = instance._defense.Block(val2, Time.unscaledTime, num, HasMeleeWeapon((Character)(object)val), HasShield((Character)(object)val));
				instance.ObserveDefenseReady(val);
				if (!(num2 <= 0f))
				{
					instance.GrantDefenseShelter(val, num2);
					Player[] array = instance.NearbyVikingAllies(val, Tuning.F("Defense.Shieldbearer/ShelterRange", 8f, 0.1f, 100f), Tuning.I("Defense.Shieldbearer/ShelterAllies", 2, 0, 32));
					foreach (Player target in array)
					{
						instance.GrantDefenseShelter(target, num2);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Character), "Awake")]
		private static class DefenseStrikeReceiverPatch
		{
			private static void Postfix(Character __instance)
			{
				Player player = (Player)(object)((__instance is Player) ? __instance : null);
				if (player == null)
				{
					return;
				}
				ZNetView view = ((Component)player).GetComponent<ZNetView>();
				if ((Object)(object)view == (Object)null || !view.IsValid())
				{
					return;
				}
				view.Register<ZDOID, float, bool>("Oathbound_DefenseStrike_1", (Action<long, ZDOID, float, bool>)delegate(long sender, ZDOID id, float lost, bool staggered)
				{
					//IL_0044: Unknown result type (might be due to invalid IL or missing references)
					if (!((Object)(object)player != (Object)(object)Player.m_localPlayer) && view.IsOwner() && !(lost <= 0f) && !float.IsNaN(lost) && !float.IsInfinity(lost))
					{
						ZNetScene instance = ZNetScene.instance;
						object obj;
						if (instance == null)
						{
							obj = null;
						}
						else
						{
							GameObject obj2 = instance.FindInstance(id);
							obj = ((obj2 != null) ? obj2.GetComponent<Character>() : null);
						}
						Character val = (Character)obj;
						ZNetView val2 = ((val != null) ? ((Component)val).GetComponent<ZNetView>() : null);
						if (!((Object)(object)val2 == (Object)null) && val2.IsValid() && val2.GetZDO().GetOwner() == sender)
						{
							Instance?.ConfirmDefenseStrike(val, lost, staggered);
						}
					}
				});
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class DefenseStrikePatch
		{
			private static void Prefix(Character __instance, out float __state)
			{
				__state = Mathf.Max(0f, __instance.GetHealth());
			}

			private static void Postfix(Character __instance, HitData hit, float __state)
			{
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Invalid comparison between Unknown and I4
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Invalid comparison between Unknown and I4
				//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e7: Invalid comparison between Unknown and I4
				//IL_0148: Unknown result type (might be due to invalid IL or missing references)
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || hit.m_ranged || ((int)hit.m_skill != 7 && (int)hit.m_skill != 11))
				{
					return;
				}
				Character attacker = hit.GetAttacker();
				Player val = (Player)(object)((attacker is Player) ? attacker : null);
				if (val == null || !EnemyHit((Character)(object)val, __instance))
				{
					return;
				}
				float num = __state - Mathf.Max(0f, __instance.GetHealth());
				if (num <= 0f)
				{
					return;
				}
				ZNetView component2 = ((Component)val).GetComponent<ZNetView>();
				if ((Object)(object)component2 == (Object)null || !component2.IsValid())
				{
					return;
				}
				object obj;
				if (!((Object)(object)val == (Object)(object)Player.m_localPlayer))
				{
					obj = component2.GetZDO().GetString("oathbound.defense.class", "");
				}
				else
				{
					Plugin? instance = Instance;
					obj = ((instance != null) ? instance._state.ClassId : null) ?? "";
				}
				string text = (string)obj;
				if ((int)hit.m_skill != ((text == "monk") ? 11 : 7))
				{
					return;
				}
				if ((Object)(object)val == (Object)(object)Player.m_localPlayer)
				{
					Instance?.ConfirmDefenseStrike(__instance, num, DefenseStaggered(__instance));
					return;
				}
				switch (text)
				{
				case "berserker":
				case "breaker":
				case "monk":
					component2.InvokeRPC("Oathbound_DefenseStrike_1", new object[3]
					{
						__instance.GetZDOID(),
						num,
						DefenseStaggered(__instance)
					});
					break;
				}
			}
		}

		[HarmonyPatch(typeof(Character), "AddStaggerDamage")]
		private static class DefenseStaggerPatch
		{
			private static void Postfix(Character __instance, bool __result)
			{
				if (__result && !__instance.IsPlayer())
				{
					DefenseStaggers.GetValue(__instance, (Character _) => new DefenseStaggerStamp()).Frame = Time.frameCount;
				}
			}

			private static bool Prefix(Character __instance, float damage)
			{
				Progression val = ActiveState(__instance, "breaker");
				int num;
				if (val != null)
				{
					num = (Instance._defense.PreventStagger(val, Time.unscaledTime, WieldsTwoHandedAxe(__instance)) ? 1 : 0);
					if (num != 0 && damage > 0f)
					{
						Player val2 = (Player)(object)((__instance is Player) ? __instance : null);
						if (val2 != null)
						{
							Instance.DefenseImpactVfx(val2, broken: false);
						}
					}
				}
				else
				{
					num = 0;
				}
				return num == 0;
			}
		}

		[HarmonyPatch(typeof(Character), "ApplyDamage")]
		private static class DefenseDamagePatch
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				int totals = 0;
				MethodInfo total = AccessTools.Method(typeof(HitData), "GetTotalDamage", (Type[])null, (Type[])null);
				MethodInfo resolve = AccessTools.Method(typeof(DefenseDamagePatch), "Resolve", (Type[])null, (Type[])null);
				foreach (CodeInstruction instruction in instructions)
				{
					yield return instruction;
					if (CodeInstructionExtensions.Calls(instruction, total))
					{
						int num = totals + 1;
						totals = num;
						if (num == 2)
						{
							yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null);
							yield return new CodeInstruction(OpCodes.Ldarg_1, (object)null);
							yield return new CodeInstruction(OpCodes.Call, (object)resolve);
						}
					}
				}
				if (totals != 2)
				{
					throw new InvalidOperationException("Native ApplyDamage total reads changed; defense hook requires review.");
				}
			}

			private static float Resolve(float damage, Character target, HitData hit)
			{
				//IL_0117: Unknown result type (might be due to invalid IL or missing references)
				//IL_011e: Invalid comparison between Unknown and I4
				//IL_0218: Unknown result type (might be due to invalid IL or missing references)
				//IL_021e: Invalid comparison between Unknown and I4
				//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
				//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
				//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
				//IL_0309: Unknown result type (might be due to invalid IL or missing references)
				//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
				//IL_0330: Unknown result type (might be due to invalid IL or missing references)
				//IL_033f: Unknown result type (might be due to invalid IL or missing references)
				//IL_031d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0407: Unknown result type (might be due to invalid IL or missing references)
				//IL_045d: Unknown result type (might be due to invalid IL or missing references)
				//IL_041f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0424: Unknown result type (might be due to invalid IL or missing references)
				//IL_0427: Unknown result type (might be due to invalid IL or missing references)
				//IL_042c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0431: Unknown result type (might be due to invalid IL or missing references)
				//IL_0438: Unknown result type (might be due to invalid IL or missing references)
				//IL_0441: Expected O, but got Unknown
				//IL_0448: Unknown result type (might be due to invalid IL or missing references)
				//IL_03b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_03da: Unknown result type (might be due to invalid IL or missing references)
				//IL_03e8: Unknown result type (might be due to invalid IL or missing references)
				Plugin instance = Instance;
				if (!((Object)(object)instance == (Object)null))
				{
					Player val = (Player)(object)((target is Player) ? target : null);
					if (val != null && ActiveState((Character)(object)val, instance._state.ClassId) != null && !((Character)val).IsDebugFlying() && !((Character)val).IsDead() && !((Character)val).IsTeleporting() && !((Character)val).InCutscene() && !CinematicsManager.IsPlaying())
					{
						ZNetView component = ((Component)val).GetComponent<ZNetView>();
						if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
						{
							return damage;
						}
						if (damage <= 0.1f || float.IsNaN(damage) || float.IsInfinity(damage))
						{
							return damage;
						}
						int num = DefenseTree.Rank(instance._state);
						if (num == 0)
						{
							return damage;
						}
						ItemData currentWeapon = ((Humanoid)val).GetCurrentWeapon();
						bool flag = true;
						switch (instance._state.ClassId)
						{
						case "huscarl":
							flag = HasMeleeWeapon((Character)(object)val);
							break;
						case "monk":
							flag = currentWeapon == null || (int)currentWeapon.m_shared.m_skillType == 11;
							break;
						case "berserker":
							flag = WieldsPairedAxes((Character)(object)val);
							break;
						case "highlander":
							flag = WieldsGreatsword((Character)(object)val);
							break;
						case "breaker":
							flag = WieldsTwoHandedAxe((Character)(object)val);
							break;
						}
						Character val2 = ((instance._state.ClassId == "hunter") ? instance.CurrentHunterPet() : ((instance._state.ClassId == "dragonsworn") ? instance.CurrentDragonPet() : null));
						bool flag2 = ((instance._state.ClassId == "hunter") ? instance.DefenseCompanionNear(val, val2, "oathbound.hunter.owner", "oathbound.hunter.token", instance._hunterToken, DefenseTree.CompanionRange(num)) : (instance._state.ClassId == "dragonsworn" && instance.DefenseCompanionNear(val, val2, "oathbound.dragon.owner", "oathbound.dragon.token", instance._dragonToken, DefenseTree.CompanionRange(num))));
						DefenseContext val3 = default(DefenseContext);
						((DefenseContext)(ref val3))..ctor(((Character)val).GetHealthPercentage(), val.GetEitr(), val.GetStamina(), ((Character)val).IsBlocking(), HasShield((Character)(object)val), (int)hit.m_hitType == 3 || (!((Character)val).IsOnGround() && !((Character)val).IsSwimming()), ((Character)val).InAttack(), ((Character)val).IsStaggering(), flag, flag2, instance._dragonFlight.Height);
						instance._defense.SetMaximumHealth(((Character)val).GetMaxHealth());
						instance._defense.Tick(instance._state, Time.unscaledTime);
						float num2 = ((instance._state.ClassId == "warlock") ? instance._defense.WardRemaining : instance._defense.GuardRemaining);
						DefenseResult val4 = instance._defense.Resolve(instance._state, Time.unscaledTime, damage, val3, instance._sessionMageShield);
						if (val4.EitrSpent > 0f)
						{
							DefenseUseEitr.Invoke(val, new object[2] { 0L, val4.EitrSpent });
						}
						if (val4.AltitudeSpent > 0f)
						{
							instance._dragonFlight.Intercept(val4.AltitudeSpent);
						}
						((DamageTypes)(ref hit.m_damage)).Modify(val4.Damage / damage);
						if (damage - val4.Damage > 0.1f)
						{
							float num3 = ((instance._state.ClassId == "warlock") ? instance._defense.WardRemaining : instance._defense.GuardRemaining);
							bool broken = ((!(instance._state.ClassId == "mage")) ? (num2 > 0f && num3 <= 0f) : (val4.EitrSpent > 0f && val.GetEitr() <= 0.01f));
							instance.DefenseImpactVfx(val, broken, (val4.Shared > 0f || val4.AltitudeSpent > 0f) ? val2 : null);
						}
						instance.ObserveDefenseReady(val);
						if (val4.Shared > 0f && (Object)(object)val2 != (Object)null)
						{
							HitData val5 = new HitData
							{
								m_point = val2.GetCenterPoint(),
								m_blockable = false,
								m_dodgeable = false
							};
							val5.m_damage.m_damage = val4.Shared;
							val2.Damage(val5);
						}
						return val4.Damage;
					}
				}
				return damage;
			}
		}

		[HarmonyPatch(typeof(Character), "Awake")]
		private static class DragonAwakePatch
		{
			private static void Postfix(Character __instance)
			{
				ZNetView view = ((Component)__instance).GetComponent<ZNetView>();
				if ((Object)(object)view == (Object)null || !view.IsValid())
				{
					return;
				}
				if (IsDragonWhelp(__instance) && (Object)(object)((Component)__instance).GetComponent<DragonWhelp>() == (Object)null)
				{
					((Component)__instance).gameObject.AddComponent<DragonWhelp>();
				}
				Player p = (Player)(object)((__instance is Player) ? __instance : null);
				if (p == null)
				{
					return;
				}
				view.Register<ZDOID, string, ZDOID, float>("Oathbound_DragonEvent_1", (Method<ZDOID, string, ZDOID, float>)delegate(long sender, ZDOID pet, string token, ZDOID target, float damage)
				{
					//IL_002a: Unknown result type (might be due to invalid IL or missing references)
					//IL_002c: Unknown result type (might be due to invalid IL or missing references)
					if ((Object)(object)p == (Object)(object)Player.m_localPlayer && view.IsOwner())
					{
						Instance?.OnDragonEvent(sender, pet, token, target, damage);
					}
				});
			}
		}

		[HarmonyPatch(typeof(Character), "OnDeath")]
		private static class DragonDeathPatch
		{
			private static void Prefix(Character __instance)
			{
				//IL_002b: 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_006d: Unknown result type (might be due to invalid IL or missing references)
				if (!IsDragonWhelp(__instance))
				{
					return;
				}
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if (component.IsOwner())
				{
					ZDO zDO = component.GetZDO();
					GameObject obj = ZNetScene.instance.FindInstance(zDO.GetZDOID("oathbound.dragon.owner"));
					if (obj != null)
					{
						obj.GetComponent<ZNetView>().InvokeRPC("Oathbound_DragonEvent_1", new object[4]
						{
							__instance.GetZDOID(),
							zDO.GetString("oathbound.dragon.token", ""),
							ZDOID.None,
							0f
						});
					}
				}
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class DragonStrikePatch
		{
			private static void Prefix(Character __instance, out float __state)
			{
				__state = __instance.GetHealth();
			}

			private static void Postfix(Character __instance, HitData hit, float __state)
			{
				//IL_0070: Unknown result type (might be due to invalid IL or missing references)
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				Character attacker = hit.GetAttacker();
				if (IsDragonWhelp(attacker) && ((Component)__instance).GetComponent<ZNetView>().IsOwner())
				{
					Player val = DragonKillOwner(attacker);
					float num = Mathf.Max(0f, __state - Mathf.Max(0f, __instance.GetHealth()));
					if (!((Object)(object)val == (Object)null) && !(num <= 0f) && EnemyHit((Character)(object)val, __instance))
					{
						((Component)val).GetComponent<ZNetView>().InvokeRPC("Oathbound_DragonEvent_1", new object[4]
						{
							attacker.GetZDOID(),
							((Component)attacker).GetComponent<ZNetView>().GetZDO().GetString("oathbound.dragon.token", ""),
							__instance.GetZDOID(),
							num
						});
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyAttack")]
		private static class DragonWhelpDamagePatch
		{
			private static void Postfix(Character ___m_character, ref HitData hitData)
			{
				if (IsDragonWhelp(___m_character))
				{
					Player? obj = DragonKillOwner(___m_character);
					ZDO val = ((obj != null) ? ((Component)obj).GetComponent<ZNetView>().GetZDO() : null);
					float totalDamage = hitData.GetTotalDamage();
					if (totalDamage > 0f)
					{
						hitData.ApplyModifier((val != null) ? (Mathf.Clamp(val.GetFloat("oathbound.dragon.damage", DragonTree.BaseDamage), DragonTree.BaseDamage, DragonTree.MaxDamage) * PetDamageScale / totalDamage) : 0f);
					}
					HitData obj2 = hitData;
					obj2.m_staggerMultiplier *= ((val != null) ? Mathf.Clamp(val.GetFloat("oathbound.dragon.stagger", 1f), 1f, DragonTree.MaxStagger) : 1f);
				}
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class DragonGuardPatch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_017a: Unknown result type (might be due to invalid IL or missing references)
				//IL_019a: Unknown result type (might be due to invalid IL or missing references)
				if (hit == null || !IsDragonWhelp(__instance))
				{
					return;
				}
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
				{
					return;
				}
				ZDO zDO = component.GetZDO();
				GameObject obj = ZNetScene.instance.FindInstance(zDO.GetZDOID("oathbound.dragon.owner"));
				Player val = ((obj != null) ? obj.GetComponent<Player>() : null);
				object obj2;
				if (val == null)
				{
					obj2 = null;
				}
				else
				{
					ZNetView component2 = ((Component)val).GetComponent<ZNetView>();
					obj2 = ((component2 != null) ? component2.GetZDO() : null);
				}
				ZDO val2 = (ZDO)obj2;
				if (val2 != null && !(val2.GetString("oathbound.dragon.token", "") != zDO.GetString("oathbound.dragon.token", "")))
				{
					float num = Mathf.Clamp(val2.GetFloat("oathbound.dragon.guard", 1f), 0f, 10f);
					if (num != 1f)
					{
						hit.ApplyModifier(num);
					}
					float num2 = val2.GetFloat("oathbound.dragon.refusal", -1f);
					DragonWhelp component3 = ((Component)__instance).GetComponent<DragonWhelp>();
					if (!(num2 < -0.5f) && !(num2 > 0.5f) && !((Object)(object)component3 == (Object)null) && !(__instance.GetHealth() <= 0f) && !(Time.unscaledTime - component3.RefusedAt < 10f) && !(hit.GetTotalDamage() < __instance.GetHealth()))
					{
						component3.RefusedAt = Time.unscaledTime;
						hit.ApplyModifier(0f);
						__instance.SetHealth(Mathf.Max(1f, __instance.GetMaxHealth() * 0.5f));
						((Component)val).GetComponent<ZNetView>().InvokeRPC("Oathbound_DragonEvent_1", new object[4]
						{
							__instance.GetZDOID(),
							zDO.GetString("oathbound.dragon.token", ""),
							ZDOID.None,
							-1f
						});
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "Load")]
		private static class DragonLoadPatch
		{
			private static void Prefix(Player __instance)
			{
				if ((Object)(object)Instance?._player == (Object)(object)__instance)
				{
					Instance.StowDragon();
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyRunStaminaDrain")]
		private static class DragonRunPatch
		{
			private static void Postfix(Character ___m_character, ref float drain)
			{
				Progression val = ActiveState(___m_character, "dragonsworn");
				if (val != null && !(drain <= 0f))
				{
					drain *= 1f - DragonTree.Value(val, (Bonus)6);
					if (val.Has("dg_chase_free") && Time.unscaledTime < Instance._dragonChaseUntil)
					{
						drain = 0f;
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifySneakStaminaUsage")]
		private static class DragonSneakPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				Progression val = ActiveState(___m_character, "dragonsworn");
				if (val != null && staminaUse > 0f)
				{
					staminaUse *= 1f - DragonTree.Value(val, (Bonus)1);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyDodgeStaminaUsage")]
		private static class DragonDodgePatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				Progression val = ActiveState(___m_character, "dragonsworn");
				if (val != null && !(staminaUse <= 0f))
				{
					staminaUse *= 1f - DragonTree.Value(val, (Bonus)5);
					if (val.Has("dg_chase_free") && Time.unscaledTime < Instance._dragonChaseUntil)
					{
						staminaUse = 0f;
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyJumpStaminaUsage")]
		private static class DragonJumpPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				Progression val = ActiveState(___m_character, "dragonsworn");
				if (val != null && staminaUse > 0f)
				{
					staminaUse *= 1f - DragonTree.Value(val, (Bonus)10);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyAttackStaminaUsage")]
		private static class DragonBowCostPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				Progression val = ActiveState(___m_character, "dragonsworn");
				Character obj = ((___m_character is Humanoid) ? ___m_character : null);
				ItemData val2 = ((obj != null) ? ((Humanoid)obj).GetCurrentWeapon() : null);
				if (val != null && val2 != null && IsDragonBow(val2.m_shared.m_skillType) && staminaUse > 0f)
				{
					staminaUse *= 1f - DragonTree.Value(val, (Bonus)12);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyAttack")]
		private static class DragonShotPatch
		{
			private static void Postfix(Character ___m_character, SkillType skill, ref HitData hitData)
			{
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_005e: Unknown result type (might be due to invalid IL or missing references)
				Progression val = (IsDragonWhelp(___m_character) ? null : ActiveState(___m_character, "dragonsworn"));
				if (val != null && IsDragonBow(skill) && !(hitData.GetTotalDamage() <= 0f))
				{
					DragonTuning val2 = default(DragonTuning);
					((DragonTuning)(ref val2))..ctor(val);
					if (val2.CeilingShot > 0f && Instance.AtCeiling())
					{
						((DamageTypes)(ref hitData.m_damage)).Modify(1f + val2.CeilingShot);
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyStaminaRegen")]
		private static class DragonStaminaPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaMultiplier)
			{
				Progression val = ActiveState(___m_character, "dragonsworn");
				if (val != null)
				{
					staminaMultiplier *= 1f + DragonTree.Value(val, (Bonus)8);
					if (val.Has("dg_over_calm") && Instance.AtCeiling())
					{
						staminaMultiplier *= 1.25f;
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyHealthRegen")]
		private static class DragonHealthPatch
		{
			private static void Postfix(Character ___m_character, ref float regenMultiplier)
			{
				Progression val = ActiveState(___m_character, "dragonsworn");
				if (val != null)
				{
					regenMultiplier *= 1f + DragonTree.Value(val, (Bonus)9);
				}
			}
		}

		[HarmonyPatch(typeof(Character), "AddStaggerDamage")]
		private static class DragonStaggerPatch
		{
			private static void Prefix(Character __instance, ref float damage)
			{
				Progression val = ActiveState(__instance, "dragonsworn");
				if (val != null && damage > 0f)
				{
					damage *= 1f - DragonTree.Value(val, (Bonus)7);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyFallDamage")]
		private static class DragonFallPatch
		{
			private static void Postfix(Character ___m_character, ref float damage)
			{
				Progression val = ActiveState(___m_character, "dragonsworn");
				if (val != null && damage > 0f && val.Has("dg_over_fall") && Instance.AtCeiling())
				{
					damage = 0f;
				}
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class DragonFrostPatch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				if (hit == null || hit.m_damage.m_frost <= 0f)
				{
					return;
				}
				Progression val = ActiveState(__instance, "dragonsworn");
				if (val != null)
				{
					float num = DragonTree.Value(val, (Bonus)29);
					if (num > 0f)
					{
						hit.m_damage.m_frost *= Mathf.Clamp01(1f - num);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Character), "Damage")]
		private static class DragonMarkPatch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0070: Unknown result type (might be due to invalid IL or missing references)
				//IL_0076: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				Plugin instance = Instance;
				Character obj = ((hit != null) ? hit.GetAttacker() : null);
				Player val = (Player)(object)((obj is Player) ? obj : null);
				if ((Object)(object)instance == (Object)null || hit == null || (Object)(object)val == (Object)null || ActiveState((Character)(object)val, "dragonsworn") == null || (int)hit.m_skill == 0 || !EnemyHit((Character)(object)Player.m_localPlayer, __instance))
				{
					return;
				}
				DragonTuning val2 = default(DragonTuning);
				((DragonTuning)(ref val2))..ctor(instance._state);
				if (instance._state.Has("dg_mark") && instance._dragonMark == __instance.GetZDOID() && Time.unscaledTime < instance._dragonMarkUntil)
				{
					hit.ApplyModifier(val2.MarkBonus);
				}
				if (instance._state.Has("dg_hoar") && instance.Rimed(__instance))
				{
					hit.ApplyModifier(val2.RimeAmp);
					if (val2.RimeShatters && !(__instance.GetMaxHealth() <= 0f) && !(__instance.GetHealth() > __instance.GetMaxHealth() / 3f))
					{
						hit.ApplyModifier(1.4f);
						__instance.GetSEMan().RemoveStatusEffect(DragonRime.Hash, true);
						instance.Notify("Shatter.");
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyAttack")]
		private static class HighlanderHeftPatch
		{
			private static void Postfix(Character ___m_character, SkillType skill, HitData hitData)
			{
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Invalid comparison between Unknown and I4
				Progression val = ActiveState(___m_character, "highlander");
				if (val != null && WieldsGreatsword(___m_character) && (int)skill == 1 && !hitData.m_ranged)
				{
					Instance._martial.OnAttack(val, (double)Time.unscaledTime);
				}
			}
		}

		[HarmonyPatch(typeof(Character), "Heal")]
		private static class MortalWoundPatch
		{
			private static bool Prefix(Character __instance, float hp)
			{
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				Plugin instance = Instance;
				if ((Object)(object)instance == (Object)null || !((Behaviour)instance).enabled || !instance.CanProgress || hp <= 0f || __instance is Player)
				{
					return true;
				}
				Progression state = instance._state;
				if (state.ClassId != "highlander" && state.ClassId != "breaker")
				{
					return true;
				}
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
				{
					return true;
				}
				return !instance._martial.Denied(((object)__instance.GetZDOID()/*cast due to .constrained prefix*/).ToString(), (double)Time.unscaledTime);
			}
		}

		[HarmonyPatch(typeof(Character), "AddPushbackForce")]
		private static class MartialPushbackPatch
		{
			private static bool Prefix(Character __instance)
			{
				Plugin instance = Instance;
				if ((Object)(object)instance == (Object)null || !((Behaviour)instance).enabled || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
				{
					return true;
				}
				Progression val = MartialState(__instance);
				if (val == null)
				{
					return true;
				}
				double num = Time.unscaledTime;
				if (val.ClassId == "highlander")
				{
					return !instance._martial.ResolveUnpushed(val, num);
				}
				if (val.ClassId == "breaker")
				{
					return !instance._martial.Unmoved(val);
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Character), "Awake")]
		private static class HunterAwakePatch
		{
			private static void Postfix(Character __instance)
			{
				ZNetView view = ((Component)__instance).GetComponent<ZNetView>();
				if ((Object)(object)view == (Object)null || !view.IsValid())
				{
					return;
				}
				if (IsHunterPet(__instance) && (Object)(object)((Component)__instance).GetComponent<HunterCompanion>() == (Object)null)
				{
					((Component)__instance).gameObject.AddComponent<HunterCompanion>();
				}
				Player p = (Player)(object)((__instance is Player) ? __instance : null);
				if (p == null)
				{
					return;
				}
				view.Register<ZDOID, string, ZDOID, float>("Oathbound_HunterEvent_1", (Method<ZDOID, string, ZDOID, float>)delegate(long sender, ZDOID pet, string token, ZDOID target, float damage)
				{
					//IL_002a: Unknown result type (might be due to invalid IL or missing references)
					//IL_002c: Unknown result type (might be due to invalid IL or missing references)
					if ((Object)(object)p == (Object)(object)Player.m_localPlayer && view.IsOwner())
					{
						Instance?.OnHunterEvent(sender, pet, token, target, damage);
					}
				});
			}
		}

		[HarmonyPatch(typeof(Character), "OnDeath")]
		private static class HunterDeathPatch
		{
			private static void Prefix(Character __instance)
			{
				//IL_002b: 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_006d: Unknown result type (might be due to invalid IL or missing references)
				if (!IsHunterPet(__instance))
				{
					return;
				}
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if (component.IsOwner())
				{
					ZDO zDO = component.GetZDO();
					GameObject obj = ZNetScene.instance.FindInstance(zDO.GetZDOID("oathbound.hunter.owner"));
					if (obj != null)
					{
						obj.GetComponent<ZNetView>().InvokeRPC("Oathbound_HunterEvent_1", new object[4]
						{
							__instance.GetZDOID(),
							zDO.GetString("oathbound.hunter.token", ""),
							ZDOID.None,
							0f
						});
					}
				}
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class HunterBitePatch
		{
			private static void Prefix(Character __instance, out float __state)
			{
				__state = __instance.GetHealth();
			}

			private static void Postfix(Character __instance, HitData hit, float __state)
			{
				//IL_0070: Unknown result type (might be due to invalid IL or missing references)
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				Character attacker = hit.GetAttacker();
				if (IsHunterPet(attacker) && ((Component)__instance).GetComponent<ZNetView>().IsOwner())
				{
					Player val = HunterKillOwner(attacker);
					float num = Mathf.Max(0f, __state - Mathf.Max(0f, __instance.GetHealth()));
					if (!((Object)(object)val == (Object)null) && !(num <= 0f) && EnemyHit((Character)(object)val, __instance))
					{
						((Component)val).GetComponent<ZNetView>().InvokeRPC("Oathbound_HunterEvent_1", new object[4]
						{
							attacker.GetZDOID(),
							((Component)attacker).GetComponent<ZNetView>().GetZDO().GetString("oathbound.hunter.token", ""),
							__instance.GetZDOID(),
							num
						});
					}
				}
			}
		}

		[HarmonyPatch(typeof(Character), "Damage")]
		private static class HunterPackPatch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
				//IL_0067: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bb: Invalid comparison between Unknown and I4
				//IL_00be: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c5: Invalid comparison between Unknown and I4
				//IL_0087: 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_00f5: Unknown result type (might be due to invalid IL or missing references)
				Plugin instance = Instance;
				Character attacker = hit.GetAttacker();
				Player val = (Player)(object)((attacker is Player) ? attacker : null);
				if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null || ActiveState((Character)(object)val, "hunter") == null || (int)hit.m_skill == 0 || !EnemyHit((Character)(object)Player.m_localPlayer, __instance))
				{
					return;
				}
				HunterTuning val2 = default(HunterTuning);
				((HunterTuning)(ref val2))..ctor(instance._state);
				if (instance._state.Has("h_pack") && instance._hunterMark == __instance.GetZDOID() && Time.unscaledTime < instance._hunterMarkUntil)
				{
					hit.ApplyModifier(val2.MarkBonus);
					if (val2.MarkStaggers)
					{
						hit.m_staggerMultiplier *= 1.5f;
					}
				}
				if (val2.LooseOnShot && ((int)hit.m_skill == 8 || (int)hit.m_skill == 14) && __instance.GetHealth() > 0f)
				{
					Character val3 = instance.CurrentHunterPet();
					if ((Object)(object)val3 != (Object)null && val3.GetHealth() > 0f)
					{
						instance.SetUponHunter(val, val3, __instance, val2, announce: false);
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyAttack")]
		private static class HunterDamagePatch
		{
			private static void Postfix(Character ___m_character, ref HitData hitData)
			{
				if (IsHunterPet(___m_character))
				{
					Player? obj = HunterKillOwner(___m_character);
					ZDO val = ((obj != null) ? ((Component)obj).GetComponent<ZNetView>().GetZDO() : null);
					float totalDamage = hitData.GetTotalDamage();
					if (totalDamage > 0f)
					{
						hitData.ApplyModifier((val != null) ? (Mathf.Clamp(val.GetFloat("oathbound.hunter.damage", HunterTree.BaseDamage), HunterTree.BaseDamage, HunterTree.MaxDamage) * PetDamageScale / totalDamage) : 0f);
					}
					HitData obj2 = hitData;
					obj2.m_staggerMultiplier *= ((val != null) ? Mathf.Clamp(val.GetFloat("oathbound.hunter.stagger", 1f), 1f, HunterTree.MaxStagger) : 1f);
				}
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class HunterGuardPatch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_017a: Unknown result type (might be due to invalid IL or missing references)
				//IL_019a: Unknown result type (might be due to invalid IL or missing references)
				if (hit == null || !IsHunterPet(__instance))
				{
					return;
				}
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
				{
					return;
				}
				ZDO zDO = component.GetZDO();
				GameObject obj = ZNetScene.instance.FindInstance(zDO.GetZDOID("oathbound.hunter.owner"));
				Player val = ((obj != null) ? obj.GetComponent<Player>() : null);
				object obj2;
				if (val == null)
				{
					obj2 = null;
				}
				else
				{
					ZNetView component2 = ((Component)val).GetComponent<ZNetView>();
					obj2 = ((component2 != null) ? component2.GetZDO() : null);
				}
				ZDO val2 = (ZDO)obj2;
				if (val2 != null && !(val2.GetString("oathbound.hunter.token", "") != zDO.GetString("oathbound.hunter.token", "")))
				{
					float num = Mathf.Clamp(val2.GetFloat("oathbound.hunter.guard", 1f), 0f, 10f);
					if (num != 1f)
					{
						hit.ApplyModifier(num);
					}
					float num2 = val2.GetFloat("oathbound.hunter.rally", -1f);
					HunterCompanion component3 = ((Component)__instance).GetComponent<HunterCompanion>();
					if (!(num2 < -0.5f) && !(num2 > 0.5f) && !((Object)(object)component3 == (Object)null) && !(__instance.GetHealth() <= 0f) && !(Time.unscaledTime - component3.RalliedAt < 10f) && !(hit.GetTotalDamage() < __instance.GetHealth()))
					{
						component3.RalliedAt = Time.unscaledTime;
						hit.ApplyModifier(0f);
						__instance.SetHealth(Mathf.Max(1f, __instance.GetMaxHealth() * 0.5f));
						((Component)val).GetComponent<ZNetView>().InvokeRPC("Oathbound_HunterEvent_1", new object[4]
						{
							__instance.GetZDOID(),
							zDO.GetString("oathbound.hunter.token", ""),
							ZDOID.None,
							-1f
						});
					}
				}
			}
		}

		[HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")]
		private static class HunterLootPatch
		{
			private static bool Prefix(CharacterDrop __instance, ref List<KeyValuePair<GameObject, int>> __result)
			{
				if (!IsOathboundCompanion(((Component)__instance).GetComponent<Character>()))
				{
					return true;
				}
				__result = new List<KeyValuePair<GameObject, int>>();
				return false;
			}
		}

		[HarmonyPatch(typeof(Procreation), "Procreate")]
		private static class HunterBreedingPatch
		{
			private static bool Prefix(Procreation __instance)
			{
				return !IsOathboundCompanion(((Component)__instance).GetComponent<Character>());
			}
		}

		[HarmonyPatch(typeof(Tameable), "RPC_Command")]
		private static class HunterCommandPatch
		{
			private static bool Prefix(Tameable __instance)
			{
				return !IsOathboundCompanion(((Component)__instance).GetComponent<Character>());
			}
		}

		[HarmonyPatch(typeof(Player), "Load")]
		private static class HunterLoadPatch
		{
			private static void Prefix(Player __instance)
			{
				if ((Object)(object)Instance?._player == (Object)(object)__instance)
				{
					Instance.StowHunter();
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyRunStaminaDrain")]
		private static class HunterRunPatch
		{
			private static void Postfix(Character ___m_character, ref float drain)
			{
				Progression val = ActiveState(___m_character, "hunter");
				if (val != null && !(drain <= 0f))
				{
					drain *= 1f - HunterTree.Value(val, (Bonus)6);
					if (val.Has("h_end_pack") && Instance.HoundNear(15f))
					{
						drain *= 0.75f;
					}
					if (val.Has("h_chase_give") && Time.unscaledTime < Instance._hunterChaseUntil)
					{
						drain = 0f;
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifySneakStaminaUsage")]
		private static class HunterSneakPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				Progression val = ActiveState(___m_character, "hunter");
				if (val != null && !(staminaUse <= 0f))
				{
					staminaUse *= 1f - HunterTree.Value(val, (Bonus)1);
					if (val.Has("h_end_pack") && Instance.HoundNear(15f))
					{
						staminaUse *= 0.75f;
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyDodgeStaminaUsage")]
		private static class HunterDodgePatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				Progression val = ActiveState(___m_character, "hunter");
				if (val != null && !(staminaUse <= 0f))
				{
					staminaUse *= 1f - HunterTree.Value(val, (Bonus)5);
					if (val.Has("h_chase_give") && Time.unscaledTime < Instance._hunterChaseUntil)
					{
						staminaUse = 0f;
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyJumpStaminaUsage")]
		private static class HunterJumpPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				Progression val = ActiveState(___m_character, "hunter");
				if (val != null && staminaUse > 0f)
				{
					staminaUse *= 1f - HunterTree.Value(val, (Bonus)10);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifySwimStaminaUsage")]
		private static class HunterSwimPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				Progression val = ActiveState(___m_character, "hunter");
				if (val != null && staminaUse > 0f)
				{
					staminaUse *= 1f - HunterTree.Value(val, (Bonus)18);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyAttackStaminaUsage")]
		private static class HunterBowCostPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				Progression val = ActiveState(___m_character, "hunter");
				Character obj = ((___m_character is Humanoid) ? ___m_character : null);
				ItemData val2 = ((obj != null) ? ((Humanoid)obj).GetCurrentWeapon() : null);
				if (val != null && val2 != null && IsHunterBow(val2.m_shared.m_skillType) && staminaUse > 0f)
				{
					staminaUse *= 1f - HunterTree.Value(val, (Bonus)12);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyAttack")]
		private static class HunterShotPatch
		{
			private static void Postfix(Character ___m_character, SkillType skill, ref HitData hitData)
			{
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				Progression val = (IsHunterPet(___m_character) ? null : ActiveState(___m_character, "hunter"));
				if (val != null && IsHunterBow(skill) && !(hitData.GetTotalDamage() <= 0f))
				{
					((DamageTypes)(ref hitData.m_damage)).Modify(1f + HunterTree.Value(val, (Bonus)4));
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyStaminaRegen")]
		private static class HunterStaminaPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaMultiplier)
			{
				Progression val = ActiveState(___m_character, "hunter");
				if (val != null)
				{
					staminaMultiplier *= 1f + HunterTree.Value(val, (Bonus)8);
					if (val.Has("h_share_wind") && Time.unscaledTime < Instance._hunterBiteUntil)
					{
						staminaMultiplier *= 1.25f;
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyHealthRegen")]
		private static class HunterHealthPatch
		{
			private static void Postfix(Character ___m_character, ref float regenMultiplier)
			{
				Progression val = ActiveState(___m_character, "hunter");
				if (val != null)
				{
					regenMultiplier *= 1f + HunterTree.Value(val, (Bonus)9);
				}
			}
		}

		[HarmonyPatch(typeof(Character), "AddStaggerDamage")]
		private static class HunterStaggerPatch
		{
			private static void Prefix(Character __instance, ref float damage)
			{
				Progression val = ActiveState(__instance, "hunter");
				if (val != null && damage > 0f)
				{
					damage *= 1f - HunterTree.Value(val, (Bonus)7);
				}
			}
		}

		private sealed class Keybind
		{
			public string Button = "";

			public string Registered = "";

			public string Label = "";

			public ConfigEntry<KeyboardShortcut> Entry;

			public ButtonConfig Config;

			public KeyboardShortcut SyncedShortcut;
		}

		[HarmonyPatch(typeof(KeyboardMouseSettings), "Initialize")]
		private static class KeybindRowsPatch
		{
			private static void Postfix(KeyboardMouseSettings __instance)
			{
				try
				{
					Instance?.AddKeybindRows(__instance);
				}
				catch (Exception ex)
				{
					Plugin? instance = Instance;
					if (instance != null)
					{
						((BaseUnityPlugin)instance).Logger.LogError((object)("Oathbound keybind rows could not be added: " + ex));
					}
				}
			}
		}

		private sealed class FirePatch
		{
			internal Vector3 Point;

			internal float Radius;

			internal float Until;

			internal float NextTick;

			internal float Damage;
		}

		[HarmonyPatch(typeof(Character), "Damage")]
		private static class MageSpellHitPatch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				//IL_0049: Invalid comparison between Unknown and I4
				//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
				//IL_0122: Unknown result type (might be due to invalid IL or missing references)
				//IL_0159: Unknown result type (might be due to invalid IL or missing references)
				//IL_0135: Unknown result type (might be due to invalid IL or missing references)
				//IL_0112: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
				//IL_0190: Unknown result type (might be due to invalid IL or missing references)
				//IL_016c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0149: Unknown result type (might be due to invalid IL or missing references)
				//IL_0202: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
				//IL_0180: Unknown result type (might be due to invalid IL or missing references)
				//IL_0256: Unknown result type (might be due to invalid IL or missing references)
				//IL_0235: Unknown result type (might be due to invalid IL or missing references)
				//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
				//IL_0272: Unknown result type (might be due to invalid IL or missing references)
				//IL_0265: Unknown result type (might be due to invalid IL or missing references)
				//IL_0369: Unknown result type (might be due to invalid IL or missing references)
				//IL_028f: Unknown result type (might be due to invalid IL or missing references)
				//IL_03a1: Unknown result type (might be due to invalid IL or missing references)
				//IL_0377: Unknown result type (might be due to invalid IL or missing references)
				//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_0310: Unknown result type (might be due to invalid IL or missing references)
				//IL_033c: Unknown result type (might be due to invalid IL or missing references)
				Plugin instance = Instance;
				Player player = default(Player);
				ref Player reference = ref player;
				Character obj = ((hit != null) ? hit.GetAttacker() : null);
				reference = (Player)(object)((obj is Player) ? obj : null);
				if ((Object)(object)instance == (Object)null || (Object)(object)player == (Object)null || (int)hit.m_skill != 9)
				{
					return;
				}
				Progression val = ActiveState((Character)(object)player, "mage");
				if (val == null || hit.GetTotalDamage() <= 0f || __instance.GetHealth() <= 0f || !EnemyHit((Character)(object)player, __instance))
				{
					return;
				}
				MageTuning val2 = default(MageTuning);
				((MageTuning)(ref val2))..ctor(val);
				ZDOID zDOID = __instance.GetZDOID();
				float unscaledTime = Time.unscaledTime;
				bool num = hit.m_damage.m_fire > 0f;
				bool flag = hit.m_damage.m_frost > 0f;
				bool flag2 = hit.m_damage.m_lightning > 0f;
				if (num && val2.BurnBonus > 0f && instance.Burning(__instance))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(1f + val2.BurnBonus);
				}
				if (flag && val2.ChillBonus > 0f && instance._mageChill.ContainsKey(zDOID))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(1f + val2.ChillBonus);
				}
				if (flag2 && val2.ChargedBonus > 0f && instance._mageCharged.ContainsKey(zDOID))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(1f + val2.ChargedBonus);
				}
				if (flag2 && val2.NearBonus > 0f && Vector3.Distance(((Component)player).transform.position, ((Component)__instance).transform.position) <= Tuning.F("Mage/CloseThunderRange", 8f, 0.1f, 100f))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(1f + val2.NearBonus);
				}
				if (val2.Shatters && instance._mageFrozen.ContainsKey(zDOID))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(Tuning.F("Mage/ShatterDamageMultiplier", 1.6f, 0f, 10f));
					instance._mageFrozen.Remove(zDOID);
					__instance.GetSEMan().RemoveStatusEffect(MageFreeze.Hash, true);
				}
				if (num && val2.Burns)
				{
					instance.BeginBurn(__instance, val2);
				}
				if (flag && val2.Chills)
				{
					instance.SlowEnemy(__instance);
					(int, float) value;
					int num2 = ((!instance._mageChill.TryGetValue(zDOID, out value) || !(unscaledTime <= value.Item2)) ? 1 : (value.Item1 + 1));
					instance._mageChill[zDOID] = (num2, unscaledTime + ((num2 >= Tuning.I("Mage/ChillHitsToFreeze", 3, 1, 32)) ? Tuning.F("Mage/ChillAfterFreezeSeconds", 4f, 0.1f, 60f) : Tuning.F("Mage/ChillSeconds", 6f, 0.1f, 60f)));
					if (val2.Freezes && num2 >= Tuning.I("Mage/ChillHitsToFreeze", 3, 1, 32))
					{
						instance.FreezeEnemy(__instance);
						instance._mageChill[zDOID] = (0, unscaledTime + Tuning.F("Mage/ChillAfterFreezeSeconds", 4f, 0.1f, 60f));
					}
				}
				if (flag2 && val2.Charges)
				{
					instance._mageCharged[zDOID] = unscaledTime + Tuning.F("Mage/ChargeSeconds", 4f, 0.1f, 60f);
				}
				if (!flag2 || !val2.Arcs || instance._mageArcing)
				{
					return;
				}
				Character val3 = (from c in Character.GetAllCharacters()
					where (Object)(object)c != (Object)null && (Object)(object)c != (Object)(object)__instance && c.GetHealth() > 0f && EnemyHit((Character)(object)player, c) && Vector3.Distance(__instance.GetCenterPoint(), c.GetCenterPoint()) <= Tuning.F("Mage/ArcRange", 6f, 0.1f, 100f) && ClearPath(__instance.GetCenterPoint(), c)
					select c).OrderBy(delegate(Character c)
				{
					//IL_0001: 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_0011: Unknown result type (might be due to invalid IL or missing references)
					//IL_0016: Unknown result type (might be due to invalid IL or missing references)
					Vector3 val4 = c.GetCenterPoint() - __instance.GetCenterPoint();
					return ((Vector3)(ref val4)).sqrMagnitude;
				}).FirstOrDefault();
				if (!((Object)(object)val3 != (Object)null))
				{
					return;
				}
				instance._mageArcing = true;
				try
				{
					instance.DealBonusHit(player, val3, hit.GetTotalDamage() * Tuning.F("Mage/ArcDamageFraction", 0.5f, 0f, 10f), bleed: false, piercing: false, fire: false, lightning: true);
				}
				finally
				{
					instance._mageArcing = false;
				}
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class MageFrailPatch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				if (hit != null && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					Progression val = ActiveState(__instance, "mage");
					if (val != null && val.Has("m_st_frail") && EnemyHit(__instance, hit.GetAttacker()))
					{
						hit.ApplyModifier(new MageTuning(val).FrailPenalty);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
		private static class MageEitrPatch
		{
			private static void Postfix(Player __instance, ref float eitr)
			{
				Progression val = ActiveState((Character)(object)__instance, "mage") ?? ActiveState((Character)(object)__instance, "warlock");
				if (val != null)
				{
					eitr += ClassMagic.Eitr(val);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyEitrRegen")]
		private static class MageRegenPatch
		{
			private static void Postfix(Character ___m_character, ref float eitrMultiplier)
			{
				Progression val = ActiveState(___m_character, "mage") ?? ActiveState(___m_character, "warlock");
				if (val != null)
				{
					eitrMultiplier *= ClassMagic.Regen(val);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyAttackStaminaUsage")]
		private static class MageMeleePatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				Progression val = ActiveState(___m_character, "mage");
				Character obj = ((___m_character is Humanoid) ? ___m_character : null);
				ItemData val2 = ((obj != null) ? ((Humanoid)obj).GetCurrentWeapon() : null);
				if (val != null && val2 != null && IsMelee(val2.m_shared.m_skillType) && staminaUse > 0f)
				{
					staminaUse *= 1f - MageTree.Value(val, (Bonus)12);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyRunStaminaDrain")]
		private static class MageRunPatch
		{
			private static void Postfix(Character ___m_character, ref float drain)
			{
				Progression val = ActiveState(___m_character, "mage");
				if (val != null && drain > 0f)
				{
					drain *= 1f - MageTree.Value(val, (Bonus)6);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyJumpStaminaUsage")]
		private static class MageJumpPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				Progression val = ActiveState(___m_character, "mage");
				if (val != null && staminaUse > 0f)
				{
					staminaUse *= 1f - MageTree.Value(val, (Bonus)10);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyStaminaRegen")]
		private static class MageStaminaPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaMultiplier)
			{
				Progression val = ActiveState(___m_character, "mage");
				if (val != null)
				{
					staminaMultiplier *= 1f + MageTree.Value(val, (Bonus)8);
				}
			}
		}

		[HarmonyPatch(typeof(Character), "AddStaggerDamage")]
		private static class MageStaggerPatch
		{
			private static void Prefix(Character __instance, ref float damage)
			{
				Progression val = ActiveState(__instance, "mage");
				if (val != null && damage > 0f)
				{
					damage *= 1f - MageTree.Value(val, (Bonus)7);
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "EquipItem")]
		private static class ClassEquipPatch
		{
			private static bool Prefix(Humanoid __instance, ItemData item, ref bool __result)
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Expected O, but got Unknown
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_0043: Expected O, but got Unknown
				if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || item == null || (Object)(object)Instance == (Object)null)
				{
					return true;
				}
				if (LoadingEquipment.Contains((Player)__instance))
				{
					return true;
				}
				Instance.EnsurePlayer((Player)__instance);
				if (Instance._loadError != null || Instance.AllowedItem(item))
				{
					return true;
				}
				__result = false;
				Instance.Notify(Instance.Refusal(item));
				return false;
			}

			private static void Postfix(Humanoid __instance, ItemData item, bool __result)
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				if (__result && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && item != null && item.m_equipped)
				{
					Instance?.DiscoverEquippedClasses((Player)__instance);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyAttack")]
		private static class MartialAttackPatch
		{
			private static void Postfix(Character ___m_character, SkillType skill, ref HitData hitData)
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00db: Invalid comparison between Unknown and I4
				Progression val = MartialState(___m_character);
				if (val == null || hitData.m_ranged || !IsMelee(skill) || hitData.GetTotalDamage() <= 0f)
				{
					return;
				}
				if (val.ClassId == "rogue" || val.ClassId == "berserker" || val.ClassId == "highlander" || val.ClassId == "breaker")
				{
					hitData.ApplyModifier(1f + MartialTrees.Value(val, (Bonus)4));
					if (val.ClassId == "berserker" || val.ClassId == "highlander" || val.ClassId == "breaker")
					{
						HitData obj = hitData;
						obj.m_staggerMultiplier *= 1f + MartialTrees.Value(val, (Bonus)13);
					}
				}
				if ((int)skill == 11 && Fists(___m_character))
				{
					hitData.m_damage.m_blunt += MartialTrees.Value(val, (Bonus)11);
					hitData.ApplyModifier(MartialCombat.MonkFistPower(val));
					HitData obj2 = hitData;
					obj2.m_staggerMultiplier *= 1f + MartialTrees.Value(val, (Bonus)13);
				}
			}
		}

		[HarmonyPatch(typeof(Character), "Damage")]
		private static class MartialHitPatch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				//IL_0047: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Invalid comparison between Unknown and I4
				//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
				//IL_0102: Unknown result type (might be due to invalid IL or missing references)
				//IL_0112: Unknown result type (might be due to invalid IL or missing references)
				//IL_0117: Unknown result type (might be due to invalid IL or missing references)
				//IL_011c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0120: Unknown result type (might be due to invalid IL or missing references)
				//IL_01de: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
				//IL_0200: Unknown result type (might be due to invalid IL or missing references)
				//IL_0205: Unknown result type (might be due to invalid IL or missing references)
				//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
				//IL_038b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0390: Unknown result type (might be due to invalid IL or missing references)
				//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
				//IL_03ac: Unknown result type (might be due to invalid IL or missing references)
				//IL_035b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0360: Unknown result type (might be due to invalid IL or missing references)
				//IL_0375: Unknown result type (might be due to invalid IL or missing references)
				//IL_037c: Unknown result type (might be due to invalid IL or missing references)
				//IL_050d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0512: Unknown result type (might be due to invalid IL or missing references)
				//IL_0443: Unknown result type (might be due to invalid IL or missing references)
				//IL_05a3: Unknown result type (might be due to invalid IL or missing references)
				//IL_05a8: Unknown result type (might be due to invalid IL or missing references)
				//IL_05d7: Unknown result type (might be due to invalid IL or missing references)
				//IL_05ee: Unknown result type (might be due to invalid IL or missing references)
				//IL_0624: Unknown result type (might be due to invalid IL or missing references)
				Player player = default(Player);
				ref Player reference = ref player;
				Character attacker = hit.GetAttacker();
				reference = (Player)(object)((attacker is Player) ? attacker : null);
				Progression val = (((Object)(object)player == (Object)null) ? null : MartialState((Character)(object)player));
				if (val == null || hit.m_ranged || !IsMelee(hit.m_skill) || hit.GetTotalDamage() <= 0f || __instance.GetHealth() <= 0f || __instance.IsDodgeInvincible() || !EnemyHit((Character)(object)player, __instance) || (val.ClassId == "monk" && ((int)hit.m_skill != 11 || !Fists((Character)(object)player))))
				{
					return;
				}
				bool flag = (Object)(object)__instance.GetBaseAI() != (Object)null && !__instance.GetBaseAI().IsAlerted();
				Vector3 forward = ((Component)__instance).transform.forward;
				Vector3 val2 = ((Component)player).transform.position - ((Component)__instance).transform.position;
				bool flag2 = Vector3.Dot(forward, ((Vector3)(ref val2)).normalized) < -0.5f;
				Plugin instance = Instance;
				bool flag3 = val.ClassId == "rogue" && !((Character)player).IsOnGround() && !((Character)player).IsSwimming();
				bool flag4 = ((val.ClassId == "rogue") ? instance.Bleeding(__instance, "q_vein") : (val.ClassId == "berserker" && instance.Bleeding(__instance, "b_rend")));
				float num = ((val.ClassId == "highlander") ? Vector3.Distance(((Component)player).transform.position, ((Component)__instance).transform.position) : 0f);
				string text = ((object)__instance.GetZDOID()/*cast due to .constrained prefix*/).ToString();
				float line = instance._martial.ExecuteLine(val, (double)Time.unscaledTime, text);
				MartialStrike val3 = instance._martial.Strike(val, (double)Time.unscaledTime, text, flag, flag2, flag3, __instance.GetHealthPercentage(), flag4, ((Character)player).GetHealthPercentage(), __instance.IsStaggering(), num);
				hit.ApplyModifier(val3.Multiplier);
				hit.m_pushForce += val3.Push;
				hit.m_staggerMultiplier *= val3.Stagger;
				if (val3.Unblockable)
				{
					hit.m_blockable = false;
				}
				if (val3.Wave)
				{
					instance.ShieldPulse(player, 3f, val3.Push, slow: false, __instance.GetCenterPoint(), __instance);
				}
				if (val3.Third)
				{
					if (val.ClassId == "monk")
					{
						hit.m_staggerMultiplier *= 2f;
					}
					instance.CriticalNotice();
				}
				if (val3.Stamina > 0f)
				{
					((Character)player).AddStamina(val3.Stamina);
				}
				if (val3.Bleed)
				{
					if (val.ClassId == "berserker")
					{
						BerserkerTuning val4 = MartialCombat.BersTune(val);
						instance.BeginBleed(__instance, hit.GetTotalDamage(), "b_rend", val4.RendCap, val4.RendTicks);
					}
					else
					{
						RogueTuning val5 = MartialCombat.RogueTune(val);
						instance.BeginBleed(__instance, hit.GetTotalDamage(), "q_vein", val5.VeinCap, val5.VeinTicks);
					}
				}
				if (val3.Mortal)
				{
					instance.Notify("Mortal wound.");
				}
				if (val3.Heal > 0f)
				{
					((Character)player).Heal(val3.Heal, false);
				}
				if (val3.Execute)
				{
					float totalDamage = hit.GetTotalDamage();
					bool flag5 = __instance.IsBoss();
					float num2 = __instance.GetHealth() - totalDamage;
					if (val3.Finish && !flag5 && num2 > 0f && num2 < __instance.GetMaxHealth() * MartialCombat.HighTune(val).FinishLine)
					{
						hit.m_damage.m_slash += num2 + 1f;
						totalDamage = hit.GetTotalDamage();
					}
					instance.CriticalNotice();
					instance.HighlanderHarvest(player, __instance, totalDamage, val3.FearPulse, val3.Harvest, line);
				}
				if (val3.Quake)
				{
					float reach = instance._martial.QuakeReach(val);
					int num3 = instance._martial.SpreadFractures(val);
					Character[] array = (from c in Character.GetAllCharacters()
						where (Object)(object)c != (Object)(object)__instance && c.GetHealth() > 0f && EnemyHit((Character)(object)player, c) && Vector3.Distance(c.GetCenterPoint(), __instance.GetCenterPoint()) <= reach
						select c).ToArray();
					foreach (Character val6 in array)
					{
						instance._martial.AddFractures(val, ((object)val6.GetZDOID()/*cast due to .constrained prefix*/).ToString(), num3, (double)Time.unscaledTime);
					}
				}
				if (val3.Push > 0f)
				{
					int num5 = instance._martial.PushFractures(val);
					if (num5 > 0)
					{
						instance._martial.AddFractures(val, text, num5, (double)Time.unscaledTime);
					}
				}
				if (val3.Shattered)
				{
					instance.CriticalNotice();
				}
				if (val3.Sweep)
				{
					MonkTuning val7 = MartialCombat.MonkTune(val);
					bool flag6 = val.ClassId == "berserker" || val.ClassId == "breaker";
					float reach2 = (flag6 ? 3f : val7.SweepReach);
					float damage = (flag6 ? Mathf.Min(10f, hit.GetTotalDamage() * 0.3f) : Mathf.Min(val7.SweepCap, hit.GetTotalDamage() * 0.35f));
					int count = (flag6 ? 1 : val7.SweepTargets);
					Character[] array = (from c in Character.GetAllCharacters()
						where (Object)(object)c != (Object)(object)__instance && c.GetHealth() > 0f && EnemyHit((Character)(object)player, c) && Vector3.Distance(c.GetCenterPoint(), __instance.GetCenterPoint()) <= reach2 && ClearPath(__instance.GetCenterPoint(), c)
						select c).OrderBy(delegate(Character c)
					{
						//IL_0006: Unknown result type (might be due to invalid IL or missing references)
						//IL_0016: Unknown result type (might be due to invalid IL or missing references)
						//IL_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)
						Vector3 val8 = ((Component)c).transform.position - ((Component)__instance).transform.position;
						return ((Vector3)(ref val8)).sqrMagnitude;
					}).Take(count).ToArray();
					foreach (Character enemy in array)
					{
						instance.DealBonusHit(player, enemy, damage, bleed: false);
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyStealth")]
		private static class RogueStealthPatch
		{
			private static void Postfix(Character ___m_character, ref float stealth)
			{
				Progression val = ActiveState(___m_character, "rogue");
				if (val != null)
				{
					stealth *= (1f - MartialTrees.Value(val, (Bonus)2)) * Instance._martial.StealthFactor(val, (double)Time.unscaledTime);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyNoise")]
		private static class RogueNoisePatch
		{
			private static void Postfix(Character ___m_character, ref float noise)
			{
				Progression val = ActiveState(___m_character, "rogue");
				if (val != null)
				{
					noise *= (1f - MartialTrees.Value(val, (Bonus)3)) * Instance._martial.NoiseFactor(val, (double)Time.unscaledTime);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifySneakStaminaUsage")]
		private static class MartialSneakPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				staminaUse *= 1f - MartialBonus(___m_character, (Bonus)1);
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyDodgeStaminaUsage")]
		private static class MartialDodgePatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				staminaUse *= 1f - MartialBonus(___m_character, (Bonus)5);
				Progression val = ActiveState(___m_character, "rogue");
				if (val != null && val.Has("q_hard_corner") && ___m_character.GetHealthPercentage() < Tuning.F("Rogue/CorneredHealthThreshold", 0.3f, 0f, 1f))
				{
					staminaUse = 0f;
				}
			}
		}

		[HarmonyPatch(typeof(Character), "GetEquipmentMovementModifier")]
		private static class MartialBurdenPatch
		{
			private static void Postfix(Character __instance, ref float __result)
			{
				if (!(__result >= 0f))
				{
					float num = MartialBonus(__instance, (Bonus)28);
					if (!(num <= 0f))
					{
						__result *= 1f - Mathf.Min(1f, num);
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyRunStaminaDrain")]
		private static class MartialRunPatch
		{
			private static void Postfix(Character ___m_character, ref float drain)
			{
				drain *= 1f - MartialBonus(___m_character, (Bonus)6);
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyJumpStaminaUsage")]
		private static class MartialJumpPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				staminaUse *= 1f - MartialBonus(___m_character, (Bonus)10);
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyStaminaRegen")]
		private static class MartialRegenPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaMultiplier)
			{
				staminaMultiplier *= 1f + MartialBonus(___m_character, (Bonus)8);
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyHealthRegen")]
		private static class MartialHealthPatch
		{
			private static void Postfix(Character ___m_character, ref float regenMultiplier)
			{
				regenMultiplier *= 1f + MartialBonus(___m_character, (Bonus)9);
			}
		}

		[HarmonyPatch(typeof(Player), "GetBodyArmor")]
		private static class MartialArmourPatch
		{
			private static void Postfix(Player __instance, ref float __result)
			{
				__result += MartialBonus((Character)(object)__instance, (Bonus)14);
				Progression val = MartialState((Character)(object)__instance);
				if (val != null)
				{
					Plugin instance = Instance;
					double num = Time.unscaledTime;
					__result += MartialCombat.ScarArmour(val, ((Character)__instance).GetHealthPercentage());
					if (val.ClassId == "highlander")
					{
						__result += instance._martial.ResolveArmour(val, num);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Character), "AddStaggerDamage")]
		private static class MartialStaggerPatch
		{
			private static void Prefix(Character __instance, ref float damage)
			{
				damage *= 1f - MartialBonus(__instance, (Bonus)7);
				Progression val = ActiveState(__instance, "monk");
				if (val != null && Instance._martial.Unmovable(val, (double)Time.unscaledTime))
				{
					damage = 0f;
				}
				Progression val2 = MartialState(__instance);
				if (val2 == null)
				{
					return;
				}
				Plugin instance = Instance;
				if (val2.ClassId == "highlander")
				{
					damage *= 1f - instance._martial.ResolveStagger(val2, (double)Time.unscaledTime);
				}
				else if (val2.ClassId == "breaker")
				{
					damage *= 1f - instance._martial.AnvilStagger(val2);
					if (instance._martial.Unmoved(val2) && !__instance.IsBoss())
					{
						damage = 0f;
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyAttackStaminaUsage")]
		private static class MonkAttackCostPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				if (!Fists(___m_character) && !WieldsPairedAxes(___m_character) && !WieldsGreatsword(___m_character) && !WieldsTwoHandedAxe(___m_character))
				{
					return;
				}
				staminaUse *= 1f - MartialBonus(___m_character, (Bonus)12);
				Progression val = MartialState(___m_character);
				if (val == null)
				{
					return;
				}
				Plugin instance = Instance;
				double num = Time.unscaledTime;
				if (val.ClassId == "highlander")
				{
					Player val2 = (Player)(object)((___m_character is Player) ? ___m_character : null);
					if (val2 != null)
					{
						staminaUse *= 1f - instance._martial.RoomDiscount(val, instance.EnemiesNear(val2, 1.5f) == 0);
						goto IL_00bf;
					}
				}
				if (val.ClassId == "breaker")
				{
					staminaUse *= 1f - instance._martial.CrowdDiscount(val, instance.NearEnemies);
				}
				goto IL_00bf;
				IL_00bf:
				if ((Fists(___m_character) && instance._martial.Focused(val, num)) || instance._martial.Tireless(val, num))
				{
					staminaUse = 0f;
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyBlockStaminaUsage")]
		private static class MonkBlockCostPatch
		{
			private static void Postfix(Character ___m_character, ref float staminaUse)
			{
				if (Fists(___m_character))
				{
					staminaUse *= 1f - MartialBonus(___m_character, (Bonus)15);
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "ModifyFallDamage")]
		private static class RogueFallPatch
		{
			private static void Postfix(Character ___m_character, ref float damage)
			{
				//IL_0039: 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)
				Progression val = ActiveState(___m_character, "rogue");
				if (val == null)
				{
					return;
				}
				float num = Instance._martial.Fall(val, (double)Time.unscaledTime, damage);
				if (!(num <= 0f))
				{
					damage -= num;
					float fallStamina = MartialCombat.RogueTune(val).FallStamina;
					if (fallStamina > 0f)
					{
						((Character)(Player)___m_character).AddStamina(fallStamina);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class BreakerResistPatch
		{
			private static void Prefix(Character __instance, HitData hit)
			{
				Plugin instance = Instance;
				if ((Object)(object)instance == (Object)null || !((Behaviour)instance).enabled || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || hit == null)
				{
					return;
				}
				Progression val = MartialState(__instance);
				if (val != null && !(val.ClassId != "breaker"))
				{
					float num = instance._martial.AnvilResistance(val);
					if (num > 0f)
					{
						hit.ApplyModifier(1f - num);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDamaged")]
		private static class MartialWoundedPatch
		{
			private static void Postfix(Player __instance, HitData hit)
			{
				//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
				Progression val = MartialState((Character)(object)__instance);
				if (val == null || ((Character)__instance).IsDead() || hit.GetTotalDamage() <= 0f || !EnemyHit((Character)(object)__instance, hit.GetAttacker()))
				{
					return;
				}
				Plugin instance = Instance;
				if (val.ClassId == "monk")
				{
					instance._martial.OnHurt(val, (double)Time.unscaledTime);
				}
				float num = instance._martial.OnWounded(val, (double)Time.unscaledTime, ((Character)__instance).GetHealthPercentage());
				if (num <= 0f)
				{
					return;
				}
				((Character)__instance).Heal(num, false);
				if (val.ClassId == "highlander")
				{
					instance.Notify("Last rites.");
				}
				else if (val.ClassId == "berserker" || val.ClassId == "breaker")
				{
					instance.Notify("Death refuses you.");
				}
				else if (!(val.ClassId != "rogue"))
				{
					RogueTuning val2 = MartialCombat.RogueTune(val);
					((Character)__instance).AddStamina(val2.SurviveStamina);
					if (val2.SurviveBreaks)
					{
						instance._martial.BeginShroud((double)Time.unscaledTime, 4.0);
						instance.BreakContact(__instance, 20f);
						instance.Notify("Smoke and mirrors.");
					}
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
		private static class MonkBlockPatch
		{
			private static void Prefix(HitData hit, out float __state)
			{
				__state = hit.GetTotalBlockableDamage();
			}

			private static void Postfix(Humanoid __instance, HitData hit, Character attacker, bool __result, float __state)
			{
				//IL_006d: Unknown result type (might be due to invalid IL or missing references)
				//IL_007b: Unknown result type (might be due to invalid IL or missing references)
				//IL_009a: Expected O, but got Unknown