Decompiled source of ChaosPolymorph v1.3.0

plugins/ChaosPolymorph.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
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 UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = "")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 ChaosPolymorph
{
	internal static class Access
	{
		public static readonly FieldRef<Character, HitData> LastHit = AccessTools.FieldRefAccess<Character, HitData>("m_lastHit");

		public static readonly FieldRef<ZNetScene, Dictionary<ZDO, ZNetView>> Instances = AccessTools.FieldRefAccess<ZNetScene, Dictionary<ZDO, ZNetView>>("m_instances");

		public static ZNetView NView(Character c)
		{
			if (!((Object)(object)c != (Object)null))
			{
				return null;
			}
			return ((Component)c).GetComponent<ZNetView>();
		}
	}
	public enum EitrCostMode
	{
		PercentOfMax,
		Flat
	}
	public enum HealthCarryMode
	{
		Pool,
		Borrowed,
		Percent
	}
	internal static class Cfg
	{
		public static ConfigEntry<string> BaseStaff;

		public static ConfigEntry<string> CraftingStation;

		public static ConfigEntry<int> StationLevel;

		public static ConfigEntry<string> Recipe;

		public static ConfigEntry<int> MaxQuality;

		public static ConfigEntry<float> BaseSeconds;

		public static ConfigEntry<float> QualityFalloff;

		public static ConfigEntry<float> MinSecondsPerLevel;

		public static ConfigEntry<float> ImpactDamage;

		public static ConfigEntry<EitrCostMode> EitrMode;

		public static ConfigEntry<float> EitrPercentOfMax;

		public static ConfigEntry<float> EitrMinimum;

		public static ConfigEntry<float> EitrFlat;

		public static ConfigEntry<float> CooldownSeconds;

		public static ConfigEntry<float> BlastRadius;

		public static ConfigEntry<int> MaxBounces;

		public static ConfigEntry<float> BouncePower;

		public static ConfigEntry<float> Gravity;

		public static ConfigEntry<float> FuseSeconds;

		public static ConfigEntry<float> Velocity;

		public static ConfigEntry<string> TierWeights;

		public static ConfigEntry<float> HigherTierMultiplier;

		public static ConfigEntry<int> UnknownTier;

		public static ConfigEntry<string> TierOverrides;

		public static ConfigEntry<string> ExcludedTargets;

		public static ConfigEntry<string> ExcludedForms;

		public static ConfigEntry<bool> ExcludeFlyingForms;

		public static ConfigEntry<bool> ExcludeWaterCreatures;

		public static ConfigEntry<bool> KnownTrophiesCountAsKills;

		public static ConfigEntry<string> VariantSuffixes;

		public static ConfigEntry<bool> AggroOnTransform;

		public static ConfigEntry<bool> AggroOnRevert;

		public static ConfigEntry<bool> DeathBreaksPolymorph;

		public static ConfigEntry<HealthCarryMode> HealthMode;

		public static ConfigEntry<bool> LetThemKeepTheirWeapons;

		public static ConfigEntry<bool> ShowMessages;

		public static ConfigEntry<string> TransformEffects;

		public static ConfigEntry<bool> Verbose;

		public static ConfigEntry<bool> HenEnabled;

		public static ConfigEntry<string> HenBaseStaff;

		public static ConfigEntry<string> HenCraftingStation;

		public static ConfigEntry<string> HenRecipe;

		public static ConfigEntry<int> HenStationLevel;

		public static ConfigEntry<int> HenMaxQuality;

		public static ConfigEntry<float> HenSeconds;

		public static ConfigEntry<float> HenCooldown;

		public static ConfigEntry<float> HenBaseRadius;

		public static ConfigEntry<float> HenRadiusPerLevel;

		public static ConfigEntry<float> HenRadiusFalloff;

		public static ConfigEntry<float> HenMinRadiusPerLevel;

		public static ConfigEntry<EitrCostMode> HenEitrMode;

		public static ConfigEntry<string> HenCastFrom;

		public static ConfigEntry<string> HenDefaultForm;

		public static ConfigEntry<string> HenEggItems;

		public static ConfigEntry<string> HenExcludedForms;

		public static ConfigEntry<float> HenMaxFormHeight;

		public static ConfigEntry<float> HenShaftThickness;

		public static ConfigEntry<bool> HenPlaySound;

		public static ConfigEntry<bool> HenPanic;

		public static ConfigEntry<bool> HenCastEffect;

		public static ConfigEntry<string> HenCastEffectFrom;

		public static ConfigEntry<string> HenParticleSources;

		public static ConfigEntry<float> HenCastEffectScale;

		public static ConfigEntry<float> HenEitrPercent;

		public static ConfigEntry<float> HenEitrMinimum;

		public static ConfigEntry<float> HenEitrFlat;

		public static ConfigEntry<bool> HenTrimHead;

		public static ConfigEntry<bool> HenAnimated;

		public static ConfigEntry<bool> HenLight;

		public static ConfigEntry<bool> HenHideOriginalEffects;

		public static ConfigEntry<bool> HenRenderIcon;

		public static ConfigEntry<float> HenTrimFraction;

		public static ConfigEntry<float> HenSize;

		public static ConfigEntry<float> HenHeightOffset;

		public static ConfigEntry<float> HenTwist;

		public static ConfigEntry<float> HenGlow;

		public static ConfigEntry<float> HenIconSpin;

		public static ConfigEntry<float> HenIconShaftShown;

		public static ConfigEntry<bool> MaskEnabled;

		public static ConfigEntry<bool> MaskSameKind;

		public static ConfigEntry<bool> MaskEndOnHit;

		public static ConfigEntry<bool> MaskAllowBosses;

		public static ConfigEntry<bool> MaskExcludeFlyers;

		public static ConfigEntry<bool> MaskPlaySound;

		public static ConfigEntry<bool> MaskCastEffect;

		public static ConfigEntry<string> MaskBaseStaff;

		public static ConfigEntry<string> MaskCraftingStation;

		public static ConfigEntry<string> MaskRecipe;

		public static ConfigEntry<string> MaskCastFrom;

		public static ConfigEntry<string> MaskDefaultForm;

		public static ConfigEntry<string> MaskEggItems;

		public static ConfigEntry<string> MaskExcludedForms;

		public static ConfigEntry<int> MaskStationLevel;

		public static ConfigEntry<int> MaskMaxQuality;

		public static ConfigEntry<float> MaskCastEitr;

		public static ConfigEntry<float> MaskDrain;

		public static ConfigEntry<float> MaskTickSeconds;

		public static ConfigEntry<float> MaskTickPerLevel;

		public static ConfigEntry<float> MaskTickFalloff;

		public static ConfigEntry<float> MaskMinTickPerLevel;

		public static ConfigEntry<float> MaskMaxHeight;

		public static ConfigEntry<float> MaskMaxLength;

		public static ConfigEntry<float> MaskToggleCooldown;

		public static ConfigEntry<float> MaskAttackLockout;

		public static ConfigEntry<float> MaskRevealRadius;

		public static ConfigEntry<float> MaskTopperSize;

		public static ConfigEntry<float> MaskGlow;

		public static ConfigEntry<bool> MaskLight;

		public static ConfigEntry<string> ModelFile;

		public static ConfigEntry<float> ModelScale;

		public static ConfigEntry<float> ModelTwist;

		public static ConfigEntry<string> ModelRotation;

		public static ConfigEntry<string> ModelOffset;

		public static ConfigEntry<bool> FlipModel;

		public static ConfigEntry<bool> ModelOutline;

		public static ConfigEntry<float> GlowStrength;

		public static ConfigEntry<bool> HideOriginalParts;

		public static ConfigEntry<bool> HideOriginalEffects;

		public static ConfigEntry<bool> RenderIcon;

		public static ConfigEntry<float> IconLean;

		public static ConfigEntry<float> IconShaftShown;

		public static ConfigEntry<float> IconSpin;

		public const string DefaultExcluded = "Serpent,Leviathan,BonemawSerpent,TrainingDummy,Skeleton_Hildir,Fenring_Cultist_Hildir,GoblinBrute_Hildir,GoblinShaman_Hildir,Skeleton_Hildir_nochest,Fenring_Cultist_Hildir_nochest,GoblinBruteBros_nochest,GoblinShaman_Hildir_nochest,Charred_Melee_Dyrnwyn";

		private static Dictionary<int, float> _tierWeights;

		private static Dictionary<string, int> _tierOverrides;

		private static HashSet<string> _excludedTargets;

		private static HashSet<string> _excludedForms;

		private static HashSet<string> _effects;

		private static HashSet<string> _variantSuffixes;

		public static readonly string[] AlwaysExcluded = new string[10] { "TrollFrost", "*_Summoned", "Summoned*", "*_Fader", "Aspect_*", "*_spiritcaller", "DvergerTest", "piece_TrainingDummy", "TrainingDummy", "BogWitchKvastur" };

		private static readonly string[] MaskAlwaysExcluded = new string[7] { "*_Summoned", "Summoned*", "*_Fader", "*_spiritcaller", "DvergerTest", "piece_TrainingDummy", "TrainingDummy" };

		public static HashSet<string> VariantSuffixSet => _variantSuffixes ?? (_variantSuffixes = ToSet(VariantSuffixes.Value));

		public static IEnumerable<string> Effects => _effects ?? (_effects = ToSet(TransformEffects.Value));

		public static void Bind(ConfigFile f)
		{
			//IL_046f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Unknown result type (might be due to invalid IL or missing references)
			//IL_047b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0488: Expected O, but got Unknown
			//IL_0488: Unknown result type (might be due to invalid IL or missing references)
			//IL_0492: Expected O, but got Unknown
			BaseStaff = B(f, "1 - Staff", "BaseStaff", "StaffGreenRoots", "Vanilla staff whose attack and projectile are cloned. StaffGreenRoots = Staff of the Wild (lobbed orb, roots removed). StaffClusterbomb = Staff of Fracturing. Restart required.");
			CraftingStation = B(f, "1 - Staff", "CraftingStation", "forge", "Crafting station prefab name (forge, piece_workbench, ...). Restart required.");
			StationLevel = B(f, "1 - Staff", "StationLevel", 1, "Minimum station level. Restart required.");
			Recipe = B(f, "1 - Staff", "Recipe", "RoundLog:10:5,Bronze:4:2,SurtlingCore:2:1,AncientSeed:1:1", "Comma list of Item:Amount:AmountPerUpgradeLevel. Default is all Black Forest materials. Restart required.");
			MaxQuality = B(f, "1 - Staff", "MaxQuality", 4, "Maximum upgrade level. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10));
			BaseSeconds = B(f, "1 - Staff", "BaseSeconds", 5f, "Transformation length at quality 1.");
			QualityFalloff = B(f, "1 - Staff", "QualityFalloff", 0.85f, "Each quality level above 1 adds this share of the previous level's bonus (diminishing returns). Defaults: Q2 9.25s, Q3 12.9s, Q4 15.9s, Q5 18.5s. 1 = no falloff.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			MinSecondsPerLevel = B(f, "1 - Staff", "MinSecondsPerLevel", 2f, "Floor for what each extra quality level adds, so very high qualities (Forge of Potential) still grow slowly.");
			ImpactDamage = B(f, "1 - Staff", "ImpactDamage", 0f, "Blunt damage dealt by the blast. 0 = pure utility, no damage.");
			EitrMode = B(f, "2 - Eitr", "CostMode", EitrCostMode.PercentOfMax, "PercentOfMax: cost is a share of the caster's max eitr (never below Minimum), so it is always castable once you have ANY eitr food from Wizardry/PoV, but it eats most of your pool. Flat: fixed cost.");
			EitrPercentOfMax = B(f, "2 - Eitr", "PercentOfMax", 0.6f, "Share of max eitr used per cast in PercentOfMax mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			EitrMinimum = B(f, "2 - Eitr", "Minimum", 20f, "Lowest possible cost in PercentOfMax mode. Keep this at or below what ONE Black Forest eitr food gives.");
			EitrFlat = B(f, "2 - Eitr", "FlatCost", 40f, "Cost per cast in Flat mode. Use chaospoly_eitr in the console to see your current max eitr.");
			CooldownSeconds = B(f, "2 - Eitr", "CooldownSeconds", 0f, "Optional cooldown between casts. 0 = off.");
			BlastRadius = B(f, "3 - Projectile v2", "BlastRadius", 4f, "Radius of the polymorph burst in metres.");
			MaxBounces = B(f, "3 - Projectile v2", "MaxBounces", 0, "0 = burst on the first thing it touches (recommended). Above 0 = bounce that many times off terrain first.");
			BouncePower = B(f, "3 - Projectile v2", "BouncePower", 0.5f, "Velocity kept on each bounce (0-1). Only used when MaxBounces > 0.");
			Gravity = B(f, "3 - Projectile v2", "Gravity", 0f, "Projectile gravity. 0 = keep the base staff's arc.");
			FuseSeconds = B(f, "3 - Projectile v2", "FuseSeconds", 0f, "Burst after this many seconds even if it hasn't hit anything. 0 = burst when the projectile's normal lifetime runs out.");
			Velocity = B(f, "3 - Projectile v2", "Velocity", 0f, "Launch speed. 0 = keep the base staff's value.");
			TierWeights = B(f, "4 - Forms", "TierWeights", "1:12,2:8,3:5,4:3,5:2,6:1.5,7:1,8:1", "Biome tier:weight. Tiers: 1 Meadows, 2 Black Forest, 3 Swamp, 4 Mountain, 5 Plains, 6 Mistlands, 7 Ashlands, 8 Deep North. Higher weight = more likely.");
			HigherTierMultiplier = B(f, "4 - Forms", "HigherTierMultiplier", 0.35f, "Extra multiplier for forms from a HIGHER tier than the target. 0 = never upgrade a mob.");
			UnknownTier = B(f, "4 - Forms", "UnknownTier", 3, "Tier used for modded creatures the mod can't place.");
			TierOverrides = B(f, "4 - Forms", "TierOverrides", "", "Prefab:Tier overrides for any creature, e.g. 'Troll:3,SomeMonstrumMob:4'.");
			ExcludedTargets = B(f, "4 - Forms", "ExcludedTargets", "Serpent,Leviathan,BonemawSerpent,TrainingDummy,Skeleton_Hildir,Fenring_Cultist_Hildir,GoblinBrute_Hildir,GoblinShaman_Hildir,Skeleton_Hildir_nochest,Fenring_Cultist_Hildir_nochest,GoblinBruteBros_nochest,GoblinShaman_Hildir_nochest,Charred_Melee_Dyrnwyn", "Extra prefabs that can never be polymorphed; * is a wildcard. Bosses, tamed creatures, summons, Gammeltroll and boss aspects are always immune.");
			ExcludedForms = B(f, "4 - Forms", "ExcludedForms", "Serpent,Leviathan,BonemawSerpent,TrainingDummy,Skeleton_Hildir,Fenring_Cultist_Hildir,GoblinBrute_Hildir,GoblinShaman_Hildir,Skeleton_Hildir_nochest,Fenring_Cultist_Hildir_nochest,GoblinBruteBros_nochest,GoblinShaman_Hildir_nochest,Charred_Melee_Dyrnwyn", "Extra prefabs never picked as a new form; * is a wildcard.");
			ExcludeFlyingForms = B(f, "4 - Forms", "ExcludeFlyingForms", def: true, "Never turn something into a flyer (Deathsquito, Drake, Bat, Gjall, Volture...).");
			ExcludeWaterCreatures = B(f, "4 - Forms", "ExcludeWaterCreatures", def: true, "Water-only creatures can't be targets or forms.");
			KnownTrophiesCountAsKills = B(f, "4 - Forms", "KnownTrophiesCountAsKills", def: true, "Creatures whose trophy you have discovered count as killed, so existing characters start with a pool.");
			VariantSuffixes = B(f, "4 - Forms", "VariantSuffixes", "sleeping,nonsleeping,noarcher,nochest,frozen,meadows,swamps,mountains,deepnorth,aspect", "Name parts that mark a variant of the same creature (Skeleton_Meadows_noarcher = Skeleton). Variants count as one shape, and a creature never turns into its own kind, original or current.");
			AggroOnTransform = B(f, "5 - Behaviour", "AggroOnTransform", def: true, "Transformed creature targets the caster.");
			AggroOnRevert = B(f, "5 - Behaviour", "AggroOnRevert", def: true, "Creature targets the caster again when it changes back.");
			HealthMode = B(f, "5 - Behaviour", "HealthModel", HealthCarryMode.Pool, "Pool: the creature keeps its own health in any body (a 600 HP Troll becomes a 600 HP Skeleton, with the Skeleton's attacks, speed and weaknesses); damage carries 1:1. Borrowed: it gets the borrowed body's health (scaled by %), and damage taken comes off its real health. Weak: killing a Skeleton-Troll only costs the Troll ~40 HP. Percent: health % carries both ways, so killing the borrowed body kills the original. Very strong.");
			DeathBreaksPolymorph = B(f, "5 - Behaviour", "DeathBreaksPolymorph", def: true, "A killing blow on a polymorphed creature turns it back, then the same hit kills the ORIGINAL creature. Other mods (Path of Valheiman XP, Monstrum, CLLC) see a normal kill of the real creature.");
			LetThemKeepTheirWeapons = f.Bind<bool>("5 - Behaviour", "LetThemKeepTheirWeapons", false, new ConfigDescription("Polymorphed creatures keep holding whatever the original was carrying, if the new body doesn't replace it. Skeletons with Troll logs. You're welcome.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				Browsable = false
			} }));
			ShowMessages = B(f, "5 - Behaviour", "ShowMessages", def: true, "Show top-left messages to the caster.", null, synced: false);
			TransformEffects = B(f, "5 - Behaviour", "TransformEffects", "vfx_odin_despawn", "Comma list of effect prefabs played on transform/revert.");
			Verbose = B(f, "5 - Behaviour", "VerboseLogging", def: false, "Detailed log output for troubleshooting: every burst (who was caught, skipped and why), the creature catalog, projectile settings and model fitting. Warnings are always logged.", null, synced: false);
			HenEnabled = B(f, "7 - Hen Staff", "Enabled", def: true, "Add the Staff of the Ember Hen. Restart required.");
			HenBaseStaff = B(f, "7 - Hen Staff", "BaseStaff", "StaffShield", "Vanilla staff cloned for the cast animation and shaft (StaffShield = Staff of Protection). Restart required.");
			HenCraftingStation = B(f, "7 - Hen Staff", "CraftingStation", "forge", "Crafting station. Restart required.");
			HenStationLevel = B(f, "7 - Hen Staff", "StationLevel", 1, "Minimum station level. Restart required.");
			HenRecipe = B(f, "7 - Hen Staff", "Recipe", "BlackMetal:8:4,LinenThread:6:3,Feathers:10:5", "Item:Amount:AmountPerLevel list (Plains materials by default). Restart required.");
			HenMaxQuality = B(f, "7 - Hen Staff", "MaxQuality", 4, "Maximum upgrade level. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10));
			HenSeconds = B(f, "7 - Hen Staff", "HenSeconds", 4f, "How long creatures stay Hens. Fixed: quality doesn't change it.");
			HenCooldown = B(f, "7 - Hen Staff", "CooldownSeconds", 45f, "Time between casts. Fixed: quality doesn't change it.");
			HenBaseRadius = B(f, "7 - Hen Staff", "BaseRadius", 4f, "Radius around the caster at quality 1, in metres.");
			HenRadiusPerLevel = B(f, "7 - Hen Staff", "RadiusPerLevel", 1.4f, "Extra radius at quality 2; later levels add less (falloff). Defaults: Q2 5.4m, Q3 6.6m, Q4 7.6m.");
			HenRadiusFalloff = B(f, "7 - Hen Staff", "RadiusFalloff", 0.85f, "Each further level adds this share of the previous level's extra radius.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			HenMinRadiusPerLevel = B(f, "7 - Hen Staff", "MinRadiusPerLevel", 0.25f, "Floor for what each extra level adds (Forge of Potential qualities).");
			HenCastFrom = B(f, "7 - Hen Staff", "CastAnimationFrom", "StaffFireball", "Staff whose (quicker) cast animation is borrowed. StaffFireball = Staff of Embers. Restart required.");
			HenDefaultForm = B(f, "7 - Hen Staff", "DefaultForm", "Hen", "Creature a staff uses before any trophy is slotted.");
			HenEggItems = B(f, "7 - Hen Staff", "EggItems", "ChickenEgg", "Items that slot the default form (eggs) when dragged onto the staff.");
			HenMaxFormHeight = B(f, "7 - Hen Staff", "MaxSlotHeight", 3f, "Creatures taller than this (metres, from their body capsule) can't be slotted. Keeps Trolls, Lox and similar out automatically; the refusal message shows a creature's height.");
			HenExcludedForms = B(f, "7 - Hen Staff", "ExcludedForms", "", "Extra creatures that can't be slotted (* wildcards). Bosses, flyers, water creatures, babies and summons are always excluded.");
			HenPlaySound = B(f, "7 - Hen Staff", "PlayCreatureSound", def: true, "Play the slotted creature's sound on cast.");
			HenPanic = B(f, "7 - Hen Staff", "Panic", def: true, "Emberized creatures run around like headless chickens, away from where you cast. Off = they just stand around.");
			HenCastEffect = B(f, "7 - Hen Staff", "CastEffect", def: true, "Show a burst on cast and a puff where each creature changes.", null, synced: false);
			HenCastEffectFrom = B(f, "7 - Hen Staff", "CastEffectFrom", "StaffFireball", "Staff whose projectile explosion is used as the cast burst.", null, synced: false);
			HenCastEffectScale = B(f, "7 - Hen Staff", "CastEffectScale", 1f, "Size multiplier for the cast burst.", null, synced: false);
			HenParticleSources = B(f, "7 - Hen Staff", "ParticleMaterialFrom", "vfx_odin_despawn,vfx_FireAddFuel,vfx_fireball_staff_explosion,fx_Torch_Carried", "Effects (first found wins) whose particle material the ember bursts borrow. Restart required.", null, synced: false);
			HenEitrMode = B(f, "7 - Hen Staff", "EitrCostMode", EitrCostMode.PercentOfMax, "PercentOfMax or Flat, like the Chaos staff.");
			HenEitrPercent = B(f, "7 - Hen Staff", "EitrPercentOfMax", 0.5f, "Share of max eitr per cast in PercentOfMax mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			HenEitrMinimum = B(f, "7 - Hen Staff", "EitrMinimum", 25f, "Lowest cost in PercentOfMax mode.");
			HenEitrFlat = B(f, "7 - Hen Staff", "EitrFlatCost", 50f, "Cost per cast in Flat mode.");
			HenTrimHead = B(f, "8 - Hen Staff Model", "TrimOriginalHead", def: true, "Remove Staff of Protection's head so only the shaft remains (only if the game's mesh allows it; otherwise the hen perches on top). Restart required.", null, synced: false);
			HenTrimFraction = B(f, "8 - Hen Staff Model", "TrimFraction", 0.22f, "Share of the staff's length trimmed off the head end.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.6f), synced: false);
			HenShaftThickness = B(f, "8 - Hen Staff Model", "PlainShaftThickness", 0.018f, "Radius of the fallback plain shaft, as a share of the staff's length.", null, synced: false);
			HenSize = B(f, "8 - Hen Staff Model", "HenSize", 0.22f, "Hen size as a share of the staff's length.", null, synced: false);
			HenHeightOffset = B(f, "8 - Hen Staff Model", "HeightOffset", 0f, "Nudge the hen along the staff, as a share of its length (negative = lower).", null, synced: false);
			HenTwist = B(f, "8 - Hen Staff Model", "Twist", 0f, "Degrees to turn the hen around the staff.", null, synced: false);
			HenAnimated = B(f, "8 - Hen Staff Model", "Animated", def: true, "Let the hen idle-animate on the staff.", null, synced: false);
			HenGlow = B(f, "8 - Hen Staff Model", "GlowStrength", 1.5f, "Ember glow brightness. 0 = off.", null, synced: false);
			HenLight = B(f, "8 - Hen Staff Model", "EmberLight", def: true, "Small warm light from the hen.", null, synced: false);
			HenHideOriginalEffects = B(f, "8 - Hen Staff Model", "HideOriginalEffects", def: true, "Hide Staff of Protection's own particles and lights.", null, synced: false);
			HenRenderIcon = B(f, "8 - Hen Staff Model", "RenderIcon", def: true, "Render a new inventory icon.", null, synced: false);
			HenIconSpin = B(f, "8 - Hen Staff Model", "IconSpin", 0f, "Icon: spin around the staff, in degrees (180 shows the creature's other side).", null, synced: false);
			HenIconShaftShown = B(f, "8 - Hen Staff Model", "IconShaftShare", 0.15f, "Icon: share of the shaft shown below the creature. Lower = more zoomed in on the creature.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.02f, 1f), synced: false);
			MaskEnabled = B(f, "9 - Masquerade Staff", "Enabled", def: true, "Add the Staff of the Masquerade (Meadows): turns YOU into the creature it holds. Restart required.");
			MaskBaseStaff = B(f, "9 - Masquerade Staff", "BaseStaff", "StaffShield", "Vanilla staff cloned for the shaft. Restart required.");
			MaskCraftingStation = B(f, "9 - Masquerade Staff", "CraftingStation", "piece_workbench", "Crafting station. Restart required.");
			MaskStationLevel = B(f, "9 - Masquerade Staff", "StationLevel", 1, "Minimum station level. Restart required.");
			MaskRecipe = B(f, "9 - Masquerade Staff", "Recipe", "Wood:10:5,Flint:6:3,LeatherScraps:6:3,Dandelion:4:2", "Item:Amount:AmountPerLevel list (Meadows materials by default). Restart required.");
			MaskMaxQuality = B(f, "9 - Masquerade Staff", "MaxQuality", 4, "Maximum upgrade level. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10));
			MaskCastFrom = B(f, "9 - Masquerade Staff", "CastAnimationFrom", "StaffFireball", "Staff whose (quick) cast animation is borrowed. Restart required.");
			MaskDefaultForm = B(f, "9 - Masquerade Staff", "DefaultForm", "Hen", "Creature a staff holds before any trophy is slotted.");
			MaskEggItems = B(f, "9 - Masquerade Staff", "EggItems", "ChickenEgg", "Items that slot the default form when dragged onto the staff.");
			MaskCastEitr = B(f, "9 - Masquerade Staff", "CastEitr", 10f, "Eitr spent to put the disguise on.");
			MaskDrain = B(f, "9 - Masquerade Staff", "DrainPerTick", 1f, "Eitr drained each tick while disguised. Eitr doesn't regenerate while disguised. Out of eitr = you change back.");
			MaskTickSeconds = B(f, "9 - Masquerade Staff", "TickSeconds", 1f, "Seconds between drain ticks at quality 1.");
			MaskTickPerLevel = B(f, "9 - Masquerade Staff", "TickSecondsPerLevel", 0.5f, "Extra seconds between ticks at quality 2; later levels add less (falloff). Defaults: Q2 1.5s, Q3 1.9s, Q4 2.2s.");
			MaskTickFalloff = B(f, "9 - Masquerade Staff", "TickFalloff", 0.8f, "Each further level adds this share of the previous level's extra seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			MaskMinTickPerLevel = B(f, "9 - Masquerade Staff", "MinTickSecondsPerLevel", 0.1f, "Floor for what each extra level adds (Forge of Potential qualities).");
			MaskMaxHeight = B(f, "9 - Masquerade Staff", "MaxDisguiseHeight", 1.9f, "Disguises taller than this (metres) are shrunk to fit: pocket Trolls. Smaller creatures keep their real size.");
			MaskMaxLength = B(f, "9 - Masquerade Staff", "MaxDisguiseLength", 2.4f, "Disguises longer than this (metres) are shrunk to fit: mini Lox.");
			MaskSameKind = B(f, "9 - Masquerade Staff", "SameKindIgnoresYou", def: true, "Creatures of the same kind as your disguise don't treat you as an enemy (and stop chasing you). Everything else still does.");
			MaskEndOnHit = B(f, "9 - Masquerade Staff", "HitEndsDisguise", def: true, "Taking any damage ends the disguise. Attacking always ends it.");
			MaskAllowBosses = B(f, "9 - Masquerade Staff", "AllowBosses", def: true, "Boss trophies can be slotted (a pocket Eikthyr).");
			MaskExcludeFlyers = B(f, "9 - Masquerade Staff", "ExcludeFlyers", def: true, "Flying creatures can't be slotted (they have no walking animations).");
			MaskExcludedForms = B(f, "9 - Masquerade Staff", "ExcludedForms", "", "Extra creatures that can't be slotted (* wildcards). Water creatures, babies and summons are always excluded.");
			MaskAttackLockout = B(f, "9 - Masquerade Staff", "AttackLockout", 1f, "Seconds after an attack ends the disguise before you can attack at all, so the click that dropped it (and Valheim's queued retry) does nothing.");
			MaskRevealRadius = B(f, "9 - Masquerade Staff", "RevealAlertRadius", 15f, "When a disguise ends, creatures of that kind within this many metres become alert to you: no free sneak attacks on the ones you fooled. 0 = off.");
			MaskToggleCooldown = B(f, "9 - Masquerade Staff", "ToggleCooldown", 1.5f, "Seconds after changing (either way) before the staff can be cast again.");
			MaskPlaySound = B(f, "9 - Masquerade Staff", "PlayCreatureSound", def: true, "Play the creature's sound when someone puts on a disguise.");
			MaskCastEffect = B(f, "9 - Masquerade Staff", "CastEffect", def: true, "Green puff when someone changes.", null, synced: false);
			MaskTopperSize = B(f, "10 - Masquerade Staff Model", "TopperSize", 0.22f, "Creature size on the staff as a share of the staff's length. Trim, twist and icon settings are shared with the Hen staff's model section.", null, synced: false);
			MaskGlow = B(f, "10 - Masquerade Staff Model", "GlowStrength", 1.2f, "Green glow brightness. 0 = off.", null, synced: false);
			MaskLight = B(f, "10 - Masquerade Staff Model", "GreenLight", def: true, "Small green light from the topper.", null, synced: false);
			ModelFile = B(f, "6 - Model", "ModelFile", "monkey_staff.glb", "A .glb next to the DLL (or a full path) used as the staff's look. Empty = keep the base staff's look. All model settings need a restart.", null, synced: false);
			ModelScale = B(f, "6 - Model", "Scale", 1f, "Size multiplier after auto-fitting the model to the base staff's length.", null, synced: false);
			ModelTwist = B(f, "6 - Model", "TwistDegrees", 180f, "Degrees to spin the model around its length (which way the face points). 180 suits the bundled monkey staff.", null, synced: false);
			ModelRotation = B(f, "6 - Model", "RotationOffset", "0,0,0", "Extra x,y,z rotation in degrees.", null, synced: false);
			ModelOffset = B(f, "6 - Model", "PositionOffset", "0,0,0", "Extra x,y,z offset in metres, e.g. slide it through your hand.", null, synced: false);
			FlipModel = B(f, "6 - Model", "FlipEnds", def: false, "Turn the model end-for-end if the head ends up at the bottom.", null, synced: false);
			ModelOutline = B(f, "6 - Model", "Outline", def: true, "Draw the model's black cartoon-outline shell, if it has one.", null, synced: false);
			GlowStrength = B(f, "6 - Model", "GlowStrength", 2f, "Brightness of the model's glowing parts. 0 = off.", null, synced: false);
			HideOriginalParts = B(f, "6 - Model", "HideOriginalParts", def: true, "Hide small leftover pieces of the base staff's model.", null, synced: false);
			HideOriginalEffects = B(f, "6 - Model", "HideOriginalEffects", def: false, "Hide the base staff's particle effects.", null, synced: false);
			RenderIcon = B(f, "6 - Model", "RenderIcon", def: true, "Render a new inventory icon from the model.", null, synced: false);
			IconLean = B(f, "6 - Model", "IconLean", -20f, "Icon: degrees the staff leans from upright. Negative = head leans left (like Staff of the Wild's icon), positive = right.", null, synced: false);
			IconShaftShown = B(f, "6 - Model", "IconShaftShown", 0.35f, "Icon: share of the staff's length shown, measured from the head down. Lower = more zoomed in on the head. 1 = whole staff.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 1f), synced: false);
			IconSpin = B(f, "6 - Model", "IconSpin", 0f, "Icon: extra spin around the staff's length, in degrees (180 shows the other side of the head).", null, synced: false);
		}

		private static ConfigEntry<T> B<T>(ConfigFile f, string section, string key, T def, string desc, AcceptableValueBase range = null, bool synced = true)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			return f.Bind<T>(section, key, def, new ConfigDescription(desc, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = synced
			} }));
		}

		public static float DurationForQuality(int quality)
		{
			int num = Math.Max(1, Math.Min(quality, 100));
			float num2 = 0f;
			for (int i = 0; i < num; i++)
			{
				num2 += ((i == 0) ? BaseSeconds.Value : Math.Max(MinSecondsPerLevel.Value, BaseSeconds.Value * (float)Math.Pow(QualityFalloff.Value, i)));
			}
			return Math.Max(0.5f, num2);
		}

		public static void InvalidateCaches()
		{
			_tierWeights = null;
			_tierOverrides = null;
			_excludedTargets = null;
			_excludedForms = null;
			_effects = null;
			_variantSuffixes = null;
		}

		public static float TierWeight(int tier)
		{
			if (_tierWeights == null)
			{
				_tierWeights = new Dictionary<int, float>();
				foreach (KeyValuePair<string, string> item in Pairs(TierWeights.Value))
				{
					if (int.TryParse(item.Key, out var result) && TryFloat(item.Value, out var f))
					{
						_tierWeights[result] = f;
					}
				}
			}
			if (_tierWeights.TryGetValue(tier, out var value))
			{
				return value;
			}
			return 1f;
		}

		public static bool TryTierOverride(string prefab, out int tier)
		{
			if (_tierOverrides == null)
			{
				_tierOverrides = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
				foreach (KeyValuePair<string, string> item in Pairs(TierOverrides.Value))
				{
					if (int.TryParse(item.Value, out var result))
					{
						_tierOverrides[item.Key] = result;
					}
				}
			}
			return _tierOverrides.TryGetValue(prefab, out tier);
		}

		public static bool IsMaskExcluded(string prefab)
		{
			if (!MatchesAny(prefab, MaskAlwaysExcluded))
			{
				return MatchesAny(prefab, ToSet(MaskExcludedForms.Value));
			}
			return true;
		}

		public static bool IsEmberExcluded(string prefab)
		{
			if (!MatchesAny(prefab, AlwaysExcluded))
			{
				return MatchesAny(prefab, ToSet(HenExcludedForms.Value));
			}
			return true;
		}

		public static bool IsExcludedTarget(string prefab)
		{
			if (!MatchesAny(prefab, AlwaysExcluded))
			{
				return MatchesAny(prefab, _excludedTargets ?? (_excludedTargets = ToSet(ExcludedTargets.Value)));
			}
			return true;
		}

		public static bool IsExcludedForm(string prefab)
		{
			if (!MatchesAny(prefab, AlwaysExcluded))
			{
				return MatchesAny(prefab, _excludedForms ?? (_excludedForms = ToSet(ExcludedForms.Value)));
			}
			return true;
		}

		private static bool MatchesAny(string name, IEnumerable<string> patterns)
		{
			foreach (string pattern in patterns)
			{
				if (Glob(name, pattern))
				{
					return true;
				}
			}
			return false;
		}

		private static bool Glob(string name, string pattern)
		{
			if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(pattern))
			{
				return false;
			}
			if (pattern.IndexOf('*') < 0)
			{
				return string.Equals(name, pattern, StringComparison.OrdinalIgnoreCase);
			}
			string[] array = pattern.Split('*');
			int startIndex = 0;
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i];
				if (text.Length != 0)
				{
					int num = name.IndexOf(text, startIndex, StringComparison.OrdinalIgnoreCase);
					if (num < 0 || (i == 0 && num != 0))
					{
						return false;
					}
					startIndex = num + text.Length;
				}
			}
			string text2 = array[^1];
			if (text2.Length != 0)
			{
				return name.EndsWith(text2, StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		public static RequirementConfig[] ParseRequirements()
		{
			return ParseRequirements(Recipe.Value);
		}

		public static RequirementConfig[] ParseRequirements(string recipe)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			List<RequirementConfig> list = new List<RequirementConfig>();
			string[] array = (recipe ?? "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Trim().Split(':');
				if (array2.Length >= 2 && array2[0].Length != 0 && int.TryParse(array2[1], out var result))
				{
					int result2 = 0;
					if (array2.Length > 2)
					{
						int.TryParse(array2[2], out result2);
					}
					list.Add(new RequirementConfig(array2[0].Trim(), result, result2, true));
				}
			}
			if (list.Count == 0)
			{
				Plugin.Log.LogWarning((object)"Recipe config parsed to nothing; the staff will be free to craft.");
			}
			return list.ToArray();
		}

		public static HashSet<string> ToSet(string csv)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrEmpty(csv))
			{
				return hashSet;
			}
			string[] array = csv.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					hashSet.Add(text);
				}
			}
			return hashSet;
		}

		private static IEnumerable<KeyValuePair<string, string>> Pairs(string csv)
		{
			if (string.IsNullOrEmpty(csv))
			{
				yield break;
			}
			string[] array = csv.Split(',');
			foreach (string text in array)
			{
				int num = text.IndexOf(':');
				if (num > 0)
				{
					yield return new KeyValuePair<string, string>(text.Substring(0, num).Trim(), text.Substring(num + 1).Trim());
				}
			}
		}

		private static bool TryFloat(string s, out float f)
		{
			return float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out f);
		}
	}
	internal static class Commands
	{
		public static void Register()
		{
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new KillsCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new EitrCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new LearnCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ForgetCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new NoCooldownCommand());
		}

		internal static void Print(string s)
		{
			Console instance = Console.instance;
			if (instance != null)
			{
				instance.Print(s);
			}
		}
	}
	internal class KillsCommand : ConsoleCommand
	{
		public override string Name => "chaospoly_pool";

		public override string Help => "List the shapes your Chaos Polymorph staff can use, with their biome tier.";

		public override void Run(string[] args)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !CreatureCatalog.EnsureBuilt())
			{
				Commands.Print("Load into a world first.");
				return;
			}
			HashSet<string> kills = KillTracker.GetKills(localPlayer);
			HashSet<string> pool = KillTracker.GetPool(localPlayer);
			foreach (string item in pool.OrderBy(CreatureCatalog.TierOf).ThenBy((string x) => x))
			{
				CreatureInfo creatureInfo = CreatureCatalog.Get(item);
				string text = ((creatureInfo == null) ? "unknown prefab" : (creatureInfo.FormEligible ? "usable" : ("blocked: " + creatureInfo.FormBlockReason)));
				string text2 = (kills.Contains(item) ? "killed" : "trophy");
				Commands.Print($"T{CreatureCatalog.TierOf(item)} {item} [family {CreatureCatalog.Family(item)}] ({text2}, {text})");
			}
			Commands.Print($"{pool.Count} shapes known.");
		}
	}
	internal class EitrCommand : ConsoleCommand
	{
		public override string Name => "chaospoly_eitr";

		public override string Help => "Show your max eitr and what one polymorph cast costs right now.";

		public override void Run(string[] args)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				Commands.Print($"Max eitr {((Character)localPlayer).GetMaxEitr():0}, cast cost {StaffRuntime.EitrCost(localPlayer):0} ({Cfg.EitrMode.Value}).");
				Commands.Print("Duration by quality: " + string.Join(", ", from q in Enumerable.Range(1, 6)
					select $"Q{q} {Cfg.DurationForQuality(q):0.#}s"));
			}
		}
	}
	internal class LearnCommand : ConsoleCommand
	{
		public override string Name => "chaospoly_learn";

		public override string Help => "[prefab] Add a shape to your pool (testing).";

		public override bool IsCheat => true;

		public override void Run(string[] args)
		{
			if (args.Length < 1 || CreatureCatalog.Get(args[0]) == null)
			{
				Commands.Print("Unknown creature prefab.");
				return;
			}
			KillTracker.Add(Player.m_localPlayer, CreatureCatalog.Get(args[0]).Name);
			Commands.Print("Learned " + args[0] + ".");
		}
	}
	internal class ForgetCommand : ConsoleCommand
	{
		public override string Name => "chaospoly_forget";

		public override string Help => "[prefab|all] Remove killed shapes from your pool (testing). Trophy-known shapes come back unless KnownTrophiesCountAsKills is off.";

		public override bool IsCheat => true;

		public override void Run(string[] args)
		{
			if (args.Length < 1)
			{
				Commands.Print("Usage: chaospoly_forget <prefab|all>");
			}
			else
			{
				Commands.Print(KillTracker.Remove(Player.m_localPlayer, args[0]) ? ("Forgot " + args[0] + ".") : "Nothing to forget.");
			}
		}
	}
	internal class NoCooldownCommand : ConsoleCommand
	{
		public override string Name => "chaospoly_nocooldown";

		public override string Help => "Toggle all the staves' cooldowns off/on for this session (testing).";

		public override bool IsCheat => true;

		public override void Run(string[] args)
		{
			HenStaff.NoCooldown = !HenStaff.NoCooldown;
			Commands.Print(HenStaff.NoCooldown ? "Staff cooldowns OFF until restart." : "Staff cooldowns back ON.");
		}
	}
	internal sealed class CreatureInfo
	{
		public string Name;

		public int Tier;

		public string TierSource;

		public bool FormEligible;

		public string FormBlockReason;

		public string TrophyToken;

		public float Height;

		public GameObject Prefab;

		public Character Char;
	}
	internal static class CreatureCatalog
	{
		private static Dictionary<string, CreatureInfo> _byName;

		private static readonly Dictionary<string, int> VanillaTiers = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
		{
			{ "Boar", 1 },
			{ "Deer", 1 },
			{ "Neck", 1 },
			{ "Greyling", 1 },
			{ "Greydwarf", 2 },
			{ "Greydwarf_Elite", 2 },
			{ "Greydwarf_Shaman", 2 },
			{ "Troll", 2 },
			{ "Skeleton", 2 },
			{ "Skeleton_NoArcher", 2 },
			{ "Skeleton_Poison", 2 },
			{ "Ghost", 2 },
			{ "Bjorn", 2 },
			{ "Draugr", 3 },
			{ "Draugr_Elite", 3 },
			{ "Draugr_Ranged", 3 },
			{ "Blob", 3 },
			{ "BlobElite", 3 },
			{ "Leech", 3 },
			{ "Surtling", 3 },
			{ "Wraith", 3 },
			{ "Abomination", 3 },
			{ "Wolf", 4 },
			{ "Hatchling", 4 },
			{ "Fenring", 4 },
			{ "Fenring_Cultist", 4 },
			{ "StoneGolem", 4 },
			{ "Ulv", 4 },
			{ "Bat", 4 },
			{ "Goblin", 5 },
			{ "GoblinArcher", 5 },
			{ "GoblinBrute", 5 },
			{ "GoblinShaman", 5 },
			{ "Lox", 5 },
			{ "Deathsquito", 5 },
			{ "BlobTar", 5 },
			{ "Unbjorn", 5 },
			{ "Seeker", 6 },
			{ "SeekerBrute", 6 },
			{ "SeekerBrood", 6 },
			{ "Tick", 6 },
			{ "Gjall", 6 },
			{ "Hare", 6 },
			{ "Dverger", 6 },
			{ "DvergerMage", 6 },
			{ "DvergerMageFire", 6 },
			{ "DvergerMageIce", 6 },
			{ "DvergerMageSupport", 6 },
			{ "Charred_Melee", 7 },
			{ "Charred_Archer", 7 },
			{ "Charred_Twitcher", 7 },
			{ "Charred_Mage", 7 },
			{ "Morgen", 7 },
			{ "Asksvin", 7 },
			{ "Volture", 7 },
			{ "FallenValkyrie", 7 },
			{ "BlobLava", 7 },
			{ "DvergerAshlands", 7 },
			{ "JotunWarrior", 8 },
			{ "JotunWarriorDualWield", 8 },
			{ "JotunWitch", 8 },
			{ "Elaking", 8 },
			{ "ElakingMole", 8 },
			{ "ElakingLantern", 8 },
			{ "Frysling", 8 },
			{ "BlobFrost", 8 },
			{ "FrostWisp", 8 },
			{ "GoblinDeepNorth", 8 },
			{ "DvergerDeepNorth", 8 },
			{ "Barka", 8 },
			{ "Moose", 8 },
			{ "Seal", 8 },
			{ "ShadowPerson", 8 },
			{ "Skeleton_DeepNorth", 8 },
			{ "TrollFrost", 8 },
			{ "Greydwarf_Frozen", 8 },
			{ "Greydwarf_Shaman_Frozen", 8 }
		};

		public static IEnumerable<CreatureInfo> All
		{
			get
			{
				if (!EnsureBuilt())
				{
					return Enumerable.Empty<CreatureInfo>();
				}
				return _byName.Values;
			}
		}

		public static void Invalidate()
		{
			_byName = null;
		}

		public static bool EnsureBuilt()
		{
			if (_byName != null)
			{
				return true;
			}
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return false;
			}
			Build();
			return true;
		}

		public static CreatureInfo Get(string prefab)
		{
			if (string.IsNullOrEmpty(prefab) || !EnsureBuilt())
			{
				return null;
			}
			_byName.TryGetValue(prefab, out var value);
			return value;
		}

		public static int TierOf(string prefab)
		{
			return Get(prefab)?.Tier ?? Cfg.UnknownTier.Value;
		}

		private static void Build()
		{
			_byName = new Dictionary<string, CreatureInfo>(StringComparer.OrdinalIgnoreCase);
			Dictionary<string, int> spawnTiers = ScanSpawnTables();
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				if (!((Object)(object)prefab == (Object)null))
				{
					Character component = prefab.GetComponent<Character>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)prefab.GetComponent<Player>() != (Object)null))
					{
						CreatureInfo creatureInfo = new CreatureInfo
						{
							Name = ((Object)prefab).name,
							Prefab = prefab,
							Char = component,
							Height = BodyHeight(prefab)
						};
						ResolveTier(creatureInfo, component, spawnTiers);
						creatureInfo.FormEligible = Eligibility.IsFormEligible(prefab, component, out creatureInfo.FormBlockReason);
						creatureInfo.TrophyToken = FindTrophy(prefab);
						_byName[((Object)prefab).name] = creatureInfo;
					}
				}
			}
			if (!Cfg.Verbose.Value)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine($"Creature catalog ({_byName.Count} creatures):");
			foreach (CreatureInfo item in from x in _byName.Values
				orderby x.Tier, x.Name
				select x)
			{
				stringBuilder.AppendLine(string.Format("  T{0} {1,-32} tier from {2,-10} form={3}", item.Tier, item.Name, item.TierSource, item.FormEligible ? "yes" : ("no (" + item.FormBlockReason + ")")));
			}
			Plugin.Log.LogInfo((object)stringBuilder.ToString());
		}

		private static float BodyHeight(GameObject go)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			float y = go.transform.localScale.y;
			CapsuleCollider component = go.GetComponent<CapsuleCollider>();
			if ((Object)(object)component != (Object)null)
			{
				return Mathf.Max(component.height, component.radius * 2f) * y;
			}
			Collider componentInChildren = go.GetComponentInChildren<Collider>(true);
			if (!((Object)(object)componentInChildren != (Object)null))
			{
				return 1f;
			}
			Bounds bounds = componentInChildren.bounds;
			return ((Bounds)(ref bounds)).size.y * y;
		}

		public static string CreatureForTrophy(string trophyToken)
		{
			if (string.IsNullOrEmpty(trophyToken) || !EnsureBuilt())
			{
				return null;
			}
			List<CreatureInfo> list = _byName.Values.Where((CreatureInfo i) => i.TrophyToken == trophyToken).ToList();
			if (list.Count == 0)
			{
				return null;
			}
			return (list.FirstOrDefault((CreatureInfo i) => string.Equals(Family(i.Name), i.Name, StringComparison.OrdinalIgnoreCase)) ?? list[0]).Name;
		}

		private static void ResolveTier(CreatureInfo info, Character ch, Dictionary<string, int> spawnTiers)
		{
			if (Cfg.TryTierOverride(info.Name, out var tier))
			{
				info.Tier = tier;
				info.TierSource = "config";
				return;
			}
			if (VanillaTiers.TryGetValue(info.Name, out tier))
			{
				info.Tier = tier;
				info.TierSource = "vanilla";
				return;
			}
			if (spawnTiers.TryGetValue(info.Name, out tier))
			{
				info.Tier = tier;
				info.TierSource = "spawns";
				return;
			}
			float health = ch.m_health;
			if (health <= 0f)
			{
				info.Tier = Cfg.UnknownTier.Value;
				info.TierSource = "unknown";
			}
			else
			{
				info.Tier = ((health <= 40f) ? 1 : ((health <= 150f) ? 2 : ((health <= 400f) ? 3 : ((health <= 800f) ? 4 : ((health <= 1500f) ? 5 : ((health <= 3000f) ? 6 : 7))))));
				info.TierSource = "health";
			}
		}

		private static Dictionary<string, int> ScanSpawnTables()
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			try
			{
				SpawnSystemList[] array = Resources.FindObjectsOfTypeAll<SpawnSystemList>();
				foreach (SpawnSystemList val in array)
				{
					if ((Object)(object)val == (Object)null || val.m_spawners == null)
					{
						continue;
					}
					foreach (SpawnData spawner in val.m_spawners)
					{
						if (spawner == null || (Object)(object)spawner.m_prefab == (Object)null)
						{
							continue;
						}
						int num = TierFromBiome(spawner.m_biome);
						if (num > 0)
						{
							string name = ((Object)spawner.m_prefab).name;
							if (!dictionary.TryGetValue(name, out var value) || num < value)
							{
								dictionary[name] = num;
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Spawn table scan failed, falling back to health tiers: " + ex.Message));
			}
			return dictionary;
		}

		private static int TierFromBiome(Biome b)
		{
			//IL_0000: 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)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if ((b & 1) != 0)
			{
				return 1;
			}
			if ((b & 8) != 0)
			{
				return 2;
			}
			if ((b & 2) != 0)
			{
				return 3;
			}
			if ((b & 4) != 0)
			{
				return 4;
			}
			if ((b & 0x10) != 0)
			{
				return 5;
			}
			if ((b & 0x200) != 0)
			{
				return 6;
			}
			if ((b & 0x20) != 0)
			{
				return 7;
			}
			if ((b & 0x40) != 0)
			{
				return 8;
			}
			return 0;
		}

		private static string FindTrophy(GameObject go)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Invalid comparison between Unknown and I4
			CharacterDrop component = go.GetComponent<CharacterDrop>();
			if ((Object)(object)component == (Object)null || component.m_drops == null)
			{
				return null;
			}
			foreach (Drop drop in component.m_drops)
			{
				ItemDrop val = (((Object)(object)drop?.m_prefab != (Object)null) ? drop.m_prefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val != (Object)null && (int)val.m_itemData.m_shared.m_itemType == 13)
				{
					return val.m_itemData.m_shared.m_name;
				}
			}
			return null;
		}

		public static string Family(string prefab)
		{
			if (string.IsNullOrEmpty(prefab))
			{
				return prefab;
			}
			IEnumerable<string> values = prefab.Split('_').Where((string part, int i) => i == 0 || !Cfg.VariantSuffixSet.Contains(part));
			return string.Join("_", values);
		}

		public static string PickForm(IEnumerable<string> pool, string targetOriginal, string targetCurrent)
		{
			int num = TierOf(targetOriginal);
			string b = Family(targetOriginal);
			string b2 = Family(targetCurrent);
			Dictionary<string, List<CreatureInfo>> dictionary = new Dictionary<string, List<CreatureInfo>>(StringComparer.OrdinalIgnoreCase);
			foreach (string item in pool)
			{
				CreatureInfo creatureInfo = Get(item);
				if (creatureInfo == null || !creatureInfo.FormEligible)
				{
					continue;
				}
				string text = Family(creatureInfo.Name);
				if (!string.Equals(text, b, StringComparison.OrdinalIgnoreCase) && !string.Equals(text, b2, StringComparison.OrdinalIgnoreCase))
				{
					if (!dictionary.TryGetValue(text, out var value))
					{
						value = (dictionary[text] = new List<CreatureInfo>());
					}
					value.Add(creatureInfo);
				}
			}
			List<KeyValuePair<string, float>> list2 = new List<KeyValuePair<string, float>>();
			float num2 = 0f;
			foreach (KeyValuePair<string, List<CreatureInfo>> item2 in dictionary)
			{
				int num3 = item2.Value.Min((CreatureInfo i) => i.Tier);
				float num4 = Cfg.TierWeight(num3);
				if (num3 > num)
				{
					num4 *= Cfg.HigherTierMultiplier.Value;
				}
				if (!(num4 <= 0f))
				{
					list2.Add(new KeyValuePair<string, float>(item2.Key, num4));
					num2 += num4;
				}
			}
			if (list2.Count == 0)
			{
				return null;
			}
			string key = list2[list2.Count - 1].Key;
			float num5 = Random.Range(0f, num2);
			foreach (KeyValuePair<string, float> item3 in list2)
			{
				num5 -= item3.Value;
				if (num5 <= 0f)
				{
					key = item3.Key;
					break;
				}
			}
			return PickVariant(key, dictionary[key]);
		}

		private static string PickVariant(string family, List<CreatureInfo> members)
		{
			CreatureInfo creatureInfo = members.FirstOrDefault((CreatureInfo m) => string.Equals(m.Name, family, StringComparison.OrdinalIgnoreCase));
			if (creatureInfo != null)
			{
				return creatureInfo.Name;
			}
			List<CreatureInfo> list = members.Where((CreatureInfo m) => m.Name.IndexOf("sleeping", StringComparison.OrdinalIgnoreCase) < 0 || m.Name.IndexOf("nonsleeping", StringComparison.OrdinalIgnoreCase) >= 0).ToList();
			List<CreatureInfo> list2 = ((list.Count > 0) ? list : members);
			return list2[Random.Range(0, list2.Count)].Name;
		}
	}
	internal static class Eligibility
	{
		private static readonly FieldInfo StartsTamedField = AccessTools.Field(typeof(Character), "m_tamed");

		public static bool IsValidTarget(Character c)
		{
			string reason;
			return IsValidTarget(c, out reason);
		}

		public static bool IsValidTarget(Character c, out string reason)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			reason = null;
			if ((Object)(object)c == (Object)null)
			{
				reason = "null";
				return false;
			}
			if (c is Player)
			{
				reason = "player";
				return false;
			}
			if (c.IsDead())
			{
				reason = "dead";
				return false;
			}
			if (c.IsTamed())
			{
				reason = "tamed";
				return false;
			}
			if (c.IsBoss())
			{
				reason = "boss";
				return false;
			}
			if ((int)c.m_faction == 0)
			{
				reason = "friendly/summon";
				return false;
			}
			if ((Object)(object)((Component)c).GetComponent<CharacterTimedDestruction>() != (Object)null)
			{
				reason = "timed summon";
				return false;
			}
			ZNetView val = Access.NView(c);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				reason = "no network view";
				return false;
			}
			string text = Polymorph.CurrentPrefabName(c);
			if (Cfg.IsExcludedTarget(Polymorph.GetOriginalPrefabName(c) ?? text) || Cfg.IsExcludedTarget(text))
			{
				reason = "excluded in config";
				return false;
			}
			if (Cfg.ExcludeWaterCreatures.Value && IsWaterOnly(((Component)c).gameObject))
			{
				reason = "water creature";
				return false;
			}
			return true;
		}

		public static bool IsEmberFormEligible(string prefab, out string reason)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			CreatureInfo creatureInfo = CreatureCatalog.Get(prefab);
			if (creatureInfo == null)
			{
				reason = "unknown creature";
				return false;
			}
			GameObject prefab2 = creatureInfo.Prefab;
			Character val = creatureInfo.Char;
			if (val.m_boss)
			{
				reason = "boss";
				return false;
			}
			if ((int)val.m_faction == 0)
			{
				reason = "summon";
				return false;
			}
			if ((Object)(object)prefab2.GetComponent<BaseAI>() == (Object)null)
			{
				reason = "no AI";
				return false;
			}
			if ((Object)(object)prefab2.GetComponent<Growup>() != (Object)null)
			{
				reason = "baby";
				return false;
			}
			if ((Object)(object)prefab2.GetComponent<CharacterTimedDestruction>() != (Object)null)
			{
				reason = "summon";
				return false;
			}
			if (val.m_flying)
			{
				reason = "flyer";
				return false;
			}
			if (IsWaterOnly(prefab2))
			{
				reason = "water creature";
				return false;
			}
			if ((Object)(object)prefab2.GetComponentInChildren<Animator>(true) == (Object)null)
			{
				reason = "no visual";
				return false;
			}
			if (creatureInfo.Height > Cfg.HenMaxFormHeight.Value)
			{
				reason = $"too big ({creatureInfo.Height:0.#}m)";
				return false;
			}
			if (Cfg.IsEmberExcluded(creatureInfo.Name))
			{
				reason = "excluded in config";
				return false;
			}
			reason = null;
			return true;
		}

		public static bool IsMaskFormEligible(string prefab, out string reason)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			reason = null;
			if (prefab == "Player")
			{
				return true;
			}
			CreatureInfo creatureInfo = CreatureCatalog.Get(prefab);
			if (creatureInfo == null)
			{
				reason = "unknown creature";
				return false;
			}
			GameObject prefab2 = creatureInfo.Prefab;
			Character val = creatureInfo.Char;
			if (val.m_boss && !Cfg.MaskAllowBosses.Value)
			{
				reason = "boss";
				return false;
			}
			if ((int)val.m_faction == 0)
			{
				reason = "summon";
				return false;
			}
			if ((Object)(object)prefab2.GetComponent<Growup>() != (Object)null)
			{
				reason = "baby";
				return false;
			}
			if ((Object)(object)prefab2.GetComponent<CharacterTimedDestruction>() != (Object)null)
			{
				reason = "summon";
				return false;
			}
			if (val.m_flying && Cfg.MaskExcludeFlyers.Value)
			{
				reason = "flyer";
				return false;
			}
			if (IsWaterOnly(prefab2))
			{
				reason = "water creature";
				return false;
			}
			if ((Object)(object)prefab2.GetComponentInChildren<Animator>(true) == (Object)null)
			{
				reason = "no visual";
				return false;
			}
			if (Cfg.IsMaskExcluded(creatureInfo.Name))
			{
				reason = "excluded in config";
				return false;
			}
			return true;
		}

		public static bool IsFormEligible(GameObject go, Character ch, out string reason)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			reason = null;
			if (ch.m_boss)
			{
				reason = "boss";
				return false;
			}
			if ((int)ch.m_faction == 0)
			{
				reason = "friendly";
				return false;
			}
			if ((Object)(object)go.GetComponent<BaseAI>() == (Object)null)
			{
				reason = "no AI";
				return false;
			}
			if ((Object)(object)go.GetComponent<Growup>() != (Object)null)
			{
				reason = "baby";
				return false;
			}
			Tameable component = go.GetComponent<Tameable>();
			if (!((Object)(object)component != (Object)null) || !component.m_startsTamed)
			{
				bool flag = default(bool);
				int num;
				if (StartsTamedField != null)
				{
					object value = StartsTamedField.GetValue(ch);
					if (value is bool)
					{
						flag = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) == 0)
				{
					if ((Object)(object)go.GetComponent<CharacterTimedDestruction>() != (Object)null)
					{
						reason = "timed summon";
						return false;
					}
					if (Cfg.ExcludeFlyingForms.Value && ch.m_flying)
					{
						reason = "flying";
						return false;
					}
					if (Cfg.ExcludeWaterCreatures.Value && IsWaterOnly(go))
					{
						reason = "water";
						return false;
					}
					if (Cfg.IsExcludedForm(((Object)go).name))
					{
						reason = "config";
						return false;
					}
					return true;
				}
			}
			reason = "starts tame";
			return false;
		}

		public static bool IsWaterOnly(GameObject go)
		{
			MonsterAI component = go.GetComponent<MonsterAI>();
			if ((Object)(object)component != (Object)null)
			{
				return component.m_avoidLand;
			}
			return false;
		}
	}
	internal sealed class GltfModel
	{
		public sealed class Part
		{
			public List<Vector3> Positions = new List<Vector3>();

			public List<Vector3> Normals = new List<Vector3>();

			public List<Vector2> Uvs = new List<Vector2>();

			public List<int> Indices = new List<int>();

			public int Material;
		}

		public sealed class MaterialInfo
		{
			public string Name;

			public Color BaseColor = Color.white;

			public int BaseTex = -1;

			public int NormalTex = -1;

			public int EmissiveTex = -1;

			public Color Emissive = Color.black;

			public float EmissiveStrength = 1f;

			public bool DoubleSided;
		}

		public readonly List<Part> Parts = new List<Part>();

		public readonly List<MaterialInfo> Materials = new List<MaterialInfo>();

		private readonly List<byte[]> _images = new List<byte[]>();

		private readonly List<int> _textureSource = new List<int>();

		private Dictionary<string, object> _json;

		private byte[] _bin;

		public static GltfModel Load(string path)
		{
			GltfModel gltfModel = new GltfModel();
			gltfModel.Read(File.ReadAllBytes(path));
			return gltfModel;
		}

		private void Read(byte[] d)
		{
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			if (d.Length < 20 || BitConverter.ToUInt32(d, 0) != 1179937895)
			{
				throw new InvalidDataException("not a .glb file");
			}
			int num;
			for (int i = 12; i + 8 <= d.Length; i += 8 + num)
			{
				num = BitConverter.ToInt32(d, i);
				switch (BitConverter.ToUInt32(d, i + 4))
				{
				case 1313821514u:
					_json = (Dictionary<string, object>)MiniJson.Parse(Encoding.UTF8.GetString(d, i + 8, num));
					break;
				case 5130562u:
					_bin = new byte[num];
					Buffer.BlockCopy(d, i + 8, _bin, 0, num);
					break;
				}
			}
			if (_json == null || _bin == null)
			{
				throw new InvalidDataException("missing JSON or BIN chunk");
			}
			foreach (Dictionary<string, object> item in List("images"))
			{
				_images.Add(item.ContainsKey("bufferView") ? View(Int(item["bufferView"])) : null);
			}
			foreach (object item2 in List("textures"))
			{
				_textureSource.Add(((Dictionary<string, object>)item2).TryGetValue("source", out var value) ? Int(value) : (-1));
			}
			foreach (object item3 in List("materials"))
			{
				Materials.Add(ReadMaterial((Dictionary<string, object>)item3));
			}
			List<object> list = List("scenes");
			object value2;
			int index = (_json.TryGetValue("scene", out value2) ? Int(value2) : 0);
			foreach (object item4 in (list.Count > 0) ? ((List<object>)((Dictionary<string, object>)list[index])["nodes"]) : new List<object>())
			{
				Walk(Int(item4), Matrix4x4.identity);
			}
		}

		private MaterialInfo ReadMaterial(Dictionary<string, object> m)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			object value;
			MaterialInfo materialInfo = new MaterialInfo
			{
				Name = (m.TryGetValue("name", out value) ? (value as string) : "")
			};
			if (m.TryGetValue("pbrMetallicRoughness", out var value2) && value2 is Dictionary<string, object> dictionary)
			{
				if (dictionary.TryGetValue("baseColorFactor", out var value3))
				{
					materialInfo.BaseColor = ToColor((List<object>)value3);
				}
				if (dictionary.TryGetValue("baseColorTexture", out var value4))
				{
					materialInfo.BaseTex = TexIndex(value4);
				}
			}
			if (m.TryGetValue("normalTexture", out var value5))
			{
				materialInfo.NormalTex = TexIndex(value5);
			}
			if (m.TryGetValue("emissiveTexture", out var value6))
			{
				materialInfo.EmissiveTex = TexIndex(value6);
			}
			if (m.TryGetValue("emissiveFactor", out var value7))
			{
				materialInfo.Emissive = ToColor((List<object>)value7);
			}
			if (m.TryGetValue("doubleSided", out var value8))
			{
				bool flag = default(bool);
				int num;
				if (value8 is bool)
				{
					flag = (bool)value8;
					num = 1;
				}
				else
				{
					num = 0;
				}
				materialInfo.DoubleSided = (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			if (m.TryGetValue("extensions", out var value9) && value9 is Dictionary<string, object> dictionary2 && dictionary2.TryGetValue("KHR_materials_emissive_strength", out var value10) && value10 is Dictionary<string, object> dictionary3 && dictionary3.TryGetValue("emissiveStrength", out var value11))
			{
				materialInfo.EmissiveStrength = (float)(double)value11;
			}
			return materialInfo;
		}

		private int TexIndex(object texInfo)
		{
			int num = Int(((Dictionary<string, object>)texInfo)["index"]);
			if (num < 0 || num >= _textureSource.Count)
			{
				return -1;
			}
			return _textureSource[num];
		}

		public byte[] Image(int index)
		{
			if (index < 0 || index >= _images.Count)
			{
				return null;
			}
			return _images[index];
		}

		private void Walk(int nodeIdx, Matrix4x4 parent)
		{
			//IL_0017: 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)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, object> dictionary = (Dictionary<string, object>)List("nodes")[nodeIdx];
			Matrix4x4 val = parent * LocalMatrix(dictionary);
			if (dictionary.TryGetValue("mesh", out var value))
			{
				foreach (object item in (List<object>)((Dictionary<string, object>)List("meshes")[Int(value)])["primitives"])
				{
					ReadPrimitive((Dictionary<string, object>)item, val);
				}
			}
			if (!dictionary.TryGetValue("children", out var value2))
			{
				return;
			}
			foreach (object item2 in (List<object>)value2)
			{
				Walk(Int(item2), val);
			}
		}

		private static Matrix4x4 LocalMatrix(Dictionary<string, object> n)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: 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_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			if (n.TryGetValue("matrix", out var value))
			{
				float[] array = ((List<object>)value).Select((object x) => (float)(double)x).ToArray();
				Matrix4x4 result = default(Matrix4x4);
				for (int num = 0; num < 4; num++)
				{
					for (int num2 = 0; num2 < 4; num2++)
					{
						((Matrix4x4)(ref result))[num2, num] = array[num * 4 + num2];
					}
				}
				return result;
			}
			object value2;
			Vector3 val = (n.TryGetValue("translation", out value2) ? ToV3((List<object>)value2) : Vector3.zero);
			Quaternion identity = Quaternion.identity;
			if (n.TryGetValue("rotation", out var value3))
			{
				float[] array2 = ((List<object>)value3).Select((object x) => (float)(double)x).ToArray();
				((Quaternion)(ref identity))..ctor(array2[0], array2[1], array2[2], array2[3]);
			}
			object value4;
			Vector3 val2 = (n.TryGetValue("scale", out value4) ? ToV3((List<object>)value4) : Vector3.one);
			return Matrix4x4.TRS(val, identity, val2);
		}

		private void ReadPrimitive(Dictionary<string, object> p, Matrix4x4 world)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: 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_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: 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_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			if (p.TryGetValue("mode", out var value) && Int(value) != 4)
			{
				return;
			}
			Dictionary<string, object> dictionary = (Dictionary<string, object>)p["attributes"];
			if (!dictionary.ContainsKey("POSITION"))
			{
				return;
			}
			int comps;
			float[] array = Floats(Int(dictionary["POSITION"]), out comps);
			int comps2;
			float[] array2 = (dictionary.ContainsKey("NORMAL") ? Floats(Int(dictionary["NORMAL"]), out comps2) : null);
			float[] array3 = (dictionary.ContainsKey("TEXCOORD_0") ? Floats(Int(dictionary["TEXCOORD_0"]), out comps2) : null);
			int num = array.Length / comps;
			Matrix4x4 inverse = ((Matrix4x4)(ref world)).inverse;
			Matrix4x4 transpose = ((Matrix4x4)(ref inverse)).transpose;
			object value2;
			Part part = new Part
			{
				Material = (p.TryGetValue("material", out value2) ? Int(value2) : (-1))
			};
			for (int i = 0; i < num; i++)
			{
				Vector3 val = ((Matrix4x4)(ref world)).MultiplyPoint3x4(new Vector3(array[i * 3], array[i * 3 + 1], array[i * 3 + 2]));
				part.Positions.Add(new Vector3(0f - val.x, val.y, val.z));
				if (array2 != null)
				{
					Vector3 val2 = ((Matrix4x4)(ref transpose)).MultiplyVector(new Vector3(array2[i * 3], array2[i * 3 + 1], array2[i * 3 + 2]));
					Vector3 normalized = ((Vector3)(ref val2)).normalized;
					part.Normals.Add(new Vector3(0f - normalized.x, normalized.y, normalized.z));
				}
				part.Uvs.Add((Vector2)((array3 != null) ? new Vector2(array3[i * 2], 1f - array3[i * 2 + 1]) : Vector2.zero));
			}
			object value3;
			int[] array4 = (p.TryGetValue("indices", out value3) ? Ints(Int(value3)) : Enumerable.Range(0, num).ToArray());
			for (int j = 0; j + 2 < array4.Length; j += 3)
			{
				part.Indices.Add(array4[j]);
				part.Indices.Add(array4[j + 2]);
				part.Indices.Add(array4[j + 1]);
			}
			Parts.Add(part);
		}

		private byte[] View(int viewIdx)
		{
			Dictionary<string, object> obj = (Dictionary<string, object>)List("bufferViews")[viewIdx];
			object value;
			int srcOffset = (obj.TryGetValue("byteOffset", out value) ? Int(value) : 0);
			int num = Int(obj["byteLength"]);
			byte[] array = new byte[num];
			Buffer.BlockCopy(_bin, srcOffset, array, 0, num);
			return array;
		}

		private void AccessorLayout(int accIdx, out int start, out int count, out int comps, out int compType, out int stride)
		{
			Dictionary<string, object> dictionary = (Dictionary<string, object>)List("accessors")[accIdx];
			Dictionary<string, object> dictionary2 = (Dictionary<string, object>)List("bufferViews")[Int(dictionary["bufferView"])];
			start = (dictionary2.TryGetValue("byteOffset", out var value) ? Int(value) : 0) + (dictionary.TryGetValue("byteOffset", out var value2) ? Int(value2) : 0);
			count = Int(dictionary["count"]);
			comps = (string)dictionary["type"] switch
			{
				"SCALAR" => 1, 
				"VEC2" => 2, 
				"VEC3" => 3, 
				"VEC4" => 4, 
				_ => 1, 
			};
			compType = Int(dictionary["componentType"]);
			int num = ((compType == 5126 || compType == 5125) ? 4 : ((compType != 5123 && compType != 5122) ? 1 : 2));
			stride = (dictionary2.TryGetValue("byteStride", out var value3) ? Int(value3) : (num * comps));
		}

		private float[] Floats(int accIdx, out int comps)
		{
			AccessorLayout(accIdx, out var start, out var count, out comps, out var compType, out var stride);
			if (compType != 5126)
			{
				throw new InvalidDataException("only float vertex attributes are supported");
			}
			float[] array = new float[count * comps];
			for (int i = 0; i < count; i++)
			{
				for (int j = 0; j < comps; j++)
				{
					array[i * comps + j] = BitConverter.ToSingle(_bin, start + i * stride + j * 4);
				}
			}
			return array;
		}

		private int[] Ints(int accIdx)
		{
			AccessorLayout(accIdx, out var start, out var count, out var _, out var compType, out var stride);
			int[] array = new int[count];
			for (int i = 0; i < count; i++)
			{
				int num = start + i * stride;
				array[i] = compType switch
				{
					5123 => BitConverter.ToUInt16(_bin, num), 
					5125 => (int)BitConverter.ToUInt32(_bin, num), 
					_ => _bin[num], 
				};
			}
			return array;
		}

		private List<object> List(string key)
		{
			if (!_json.TryGetValue(key, out var value))
			{
				return new List<object>();
			}
			return (List<object>)value;
		}

		private static int Int(object o)
		{
			return Convert.ToInt32(o);
		}

		private static Vector3 ToV3(List<object> l)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3((float)(double)l[0], (float)(double)l[1], (float)(double)l[2]);
		}

		private static Color ToColor(List<object> l)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			return new Color((float)(double)l[0], (float)(double)l[1], (float)(double)l[2], (l.Count > 3) ? ((float)(double)l[3]) : 1f);
		}
	}
	internal static class HenStaff
	{
		public const string Prefab = "StaffEmberHen";

		public const string Token = "$item_staffemberhen";

		public const string RPC_HenNova = "ChaosPolymorph_HenNova";

		public const string RPC_EmberFx = "ChaosPolymorph_EmberFx";

		public const string RPC_FormPuff = "ChaosPolymorph_FormPuff";

		public const string ItemFormKey = "ChaosPolymorph_EmberForm";

		public static readonly int PlayerFormKey = StringExtensionMethods.GetStableHashCode("ChaosPolymorph_EmberForm");

		private static float _lastCast = -9999f;

		public static bool NoCooldown;

		public static bool IsStaff(ItemData item)
		{
			if (item != null && item.m_shared != null)
			{
				return item.m_shared.m_name == "$item_staffemberhen";
			}
			return false;
		}

		public static string FormOf(ItemData item)
		{
			if (item?.m_customData != null && item.m_customData.TryGetValue("ChaosPolymorph_EmberForm", out var value) && !string.IsNullOrEmpty(value))
			{
				return value;
			}
			return Cfg.HenDefaultForm.Value;
		}

		public static void Register()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			if (!Cfg.HenEnabled.Value)
			{
				return;
			}
			string value = Cfg.HenBaseStaff.Value;
			GameObject prefab = PrefabManager.Instance.GetPrefab(value);
			if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<ItemDrop>() == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Hen staff: base staff '" + value + "' not found; Hen staff not registered."));
				return;
			}
			ItemConfig val = new ItemConfig
			{
				Name = "$item_staffemberhen",
				Description = "$item_staffemberhen_description",
				CraftingStation = Cfg.HenCraftingStation.Value,
				MinStationLevel = Cfg.HenStationLevel.Value,
				Requirements = Cfg.ParseRequirements(Cfg.HenRecipe.Value)
			};
			CustomItem val2 = new CustomItem("StaffEmberHen", value, val);
			SharedData shared = val2.ItemDrop.m_itemData.m_shared;
			shared.m_maxQuality = Mathf.Max(1, Cfg.HenMaxQuality.Value);
			shared.m_damages = default(DamageTypes);
			shared.m_damagesPerLevel = default(DamageTypes);
			MakeQuietCast(shared, Cfg.HenCastFrom.Value, value, "Hen staff");
			try
			{
				HenModel.Apply(val2.ItemPrefab, shared, StaffStyle.Ember);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"Ember topper failed, using the plain base staff look: {arg}");
			}
			ItemManager.Instance.AddItem(val2);
			Plugin.Log.LogInfo((object)("Registered StaffEmberHen (cloned from " + value + ")."));
		}

		public static void MakeQuietCast(SharedData shared, string castFromName, string baseName, string label)
		{
			GameObject prefab = PrefabManager.Instance.GetPrefab(castFromName);
			Attack val = ((!((Object)(object)prefab != (Object)null)) ? null : prefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_attack);
			if (val != null)
			{
				shared.m_attack = val.Clone();
				Plugin.Log.LogInfo((object)(label + " uses " + ((Object)prefab).name + "'s cast animation '" + shared.m_attack.m_attackAnimation + "'."));
			}
			else
			{
				Plugin.Log.LogWarning((object)(label + ": cast source '" + castFromName + "' not found; keeping " + baseName + "'s cast."));
			}
			Attack attack = shared.m_attack;
			attack.m_spawnOnTrigger = null;
			attack.m_attackProjectile = null;
			attack.m_spawnOnHit = null;
			attack.m_projectiles = 1;
			shared.m_consumeStatusEffect = null;
			SetEnum(attack, "m_attackType", "None");
			Set(shared, "m_attackStatusEffect", null);
			if (shared.m_secondaryAttack != null)
			{
				shared.m_secondaryAttack.m_spawnOnTrigger = null;
				shared.m_secondaryAttack.m_attackProjectile = null;
				SetEnum(shared.m_secondaryAttack, "m_attackType", "None");
			}
		}

		public static float Radius(int quality)
		{
			int num = Math.Max(1, Math.Min(quality, 100));
			float num2 = Cfg.HenBaseRadius.Value;
			for (int i = 1; i < num; i++)
			{
				num2 += Math.Max(Cfg.HenMinRadiusPerLevel.Value, Cfg.HenRadiusPerLevel.Value * (float)Math.Pow(Cfg.HenRadiusFalloff.Value, i - 1));
			}
			return num2;
		}

		public static float EitrCost(Player p)
		{
			if (Cfg.HenEitrMode.Value == EitrCostMode.Flat)
			{
				return Mathf.Max(0f, Cfg.HenEitrFlat.Value);
			}
			return Mathf.Max(Cfg.HenEitrMinimum.Value, ((Character)p).GetMaxEitr() * Cfg.HenEitrPercent.Value);
		}

		public static bool BeforeAttack(Player p, ItemData weapon)
		{
			float num = (NoCooldown ? 0f : Cfg.HenCooldown.Value);
			float num2 = num - (Time.time - _lastCast);
			if (num > 0f && num2 > 0f)
			{
				if (Cfg.ShowMessages.Value)
				{
					((Character)p).Message((MessageType)1, $"The staff is still smouldering ({num2:0}s)", 0, (Sprite)null, false);
				}
				return false;
			}
			weapon.m_shared.m_attack.m_attackEitr = EitrCost(p);
			return true;
		}

		public static void AfterAttackStarted()
		{
			_lastCast = Time.time;
		}

		public static void Nova(Player caster, ItemData weapon)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			if (!CreatureCatalog.EnsureBuilt())
			{
				return;
			}
			string text = FormOf(weapon);
			if (!Eligibility.IsEmberFormEligible(text, out var reason))
			{
				Caster.Debug("slotted form " + text + " is not allowed (" + reason + "); using " + Cfg.HenDefaultForm.Value);
				text = Cfg.HenDefaultForm.Value;
			}
			float num = Radius(weapon?.m_quality ?? 1);
			float num2 = Mathf.Max(0.5f, Cfg.HenSeconds.Value);
			List<Character> list = Reach.InRange(((Component)caster).transform.position, num);
			int num3 = 0;
			foreach (Character item in list)
			{
				string reason2 = "caster";
				if ((Object)(object)item == (Object)(object)caster || !Eligibility.IsValidTarget(item, out reason2))
				{
					if ((Object)(object)item != (Object)null && !(item is Player))
					{
						Caster.Debug("  ember nova skip " + Polymorph.CurrentPrefabName(item) + ": " + reason2);
					}
				}
				else
				{
					Access.NView(item).InvokeRPC("ChaosPolymorph_HenNova", new object[3]
					{
						((Character)caster).GetZDOID(),
						num2,
						text
					});
					num3++;
				}
			}
			Caster.Debug($"Ember nova ({text}) r={num:0.#}m dur={num2}s caught {num3}");
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ChaosPolymorph_EmberFx", new object[3]
			{
				text,
				((Component)caster).transform.position,
				num
			});
			if (Cfg.ShowMessages.Value && num3 > 0)
			{
				((Character)caster).Message((MessageType)1, (num3 == 1) ? "1 creature emberized" : $"{num3} creatures emberized", 0, (Sprite)null, false);
			}
		}

		public static void HandleEmberFx(long sender, string form, Vector3 pos, float radius)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)ZNetScene.instance == (Object)null))
			{
				Caster.Debug(string.Format("ember cast fx received ({0}, r={1:0.#}, effects={2})", form, radius, Cfg.HenCastEffect.Value ? "on" : "off"));
				if (Cfg.HenCastEffect.Value)
				{
					EmberFx.Burst(pos, radius, ring: true);
					GameObject prefab = ZNetScene.instance.GetPrefab(Cfg.HenCastEffectFrom.Value);
					GameObject val = ((!((Object)(object)prefab != (Object)null)) ? null : prefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_attack.m_attackProjectile);
					Fx.Play((((Object)(object)val != (Object)null) ? val.GetComponent<Projectile>() : null)?.m_hitEffects, pos, Quaternion.identity, Mathf.Max(0.5f, radius / 3f) * Cfg.HenCastEffectScale.Value);
				}
				if (Cfg.HenPlaySound.Value)
				{
					Fx.CreatureSound(form, pos, sender == ZDOMan.GetSessionID());
				}
			}
		}

		public static void HandleFormPuff(long sender, string form, Vector3 pos)
		{
			//IL_001a: 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_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			if (Cfg.HenCastEffect.Value && !((Object)(object)ZNetScene.instance == (Object)null))
			{
				EmberFx.Burst(pos + Vector3.up * 0.5f, 1.2f, ring: false);
				GameObject prefab = ZNetScene.instance.GetPrefab(form);
				Character val = ((prefab != null) ? prefab.GetComponent<Character>() : null);
				if ((Object)(object)val != (Object)null)
				{
					Fx.Play(val.m_hitEffects, pos + Vector3.up * 0.5f, Quaternion.identity, 1f);
				}
			}
		}

		public static string FormFromItem(ItemData dragged)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Invalid comparison between Unknown and I4
			if (dragged?.m_shared == null)
			{
				return null;
			}
			string item = (((Object)(object)dragged.m_dropPrefab != (Object)null) ? ((Object)dragged.m_dropPrefab).name : "");
			if (Cfg.ToSet(Cfg.HenEggItems.Value).Contains(item))
			{
				return Cfg.HenDefaultForm.Value;
			}
			if ((int)dragged.m_shared.m_itemType != 13)
			{
				return null;
			}
			return CreatureCatalog.CreatureForTrophy(dragged.m_shared.m_name);
		}

		public static bool TrySlot(Player p, ItemData staff, ItemData dragged, Inventory fromInventory)
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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_00e6: Unknown result type (might be due to invalid IL or missing references)
			string text = FormFromItem(dragged);
			if (text == null)
			{
				return false;
			}
			if (!Eligibility.IsEmberFormEligible(text, out var reason))
			{
				((Character)p).Message((MessageType)2, "The staff can't hold that creature (" + reason + ").", 0, (Sprite)null, false);
				return true;
			}
			if (text == FormOf(staff))
			{
				((Character)p).Message((MessageType)2, "The staff already holds that creature.", 0, (Sprite)null, false);
				return true;
			}
			if (fromInventory != null)
			{
				fromInventory.RemoveItem(dragged, 1);
			}
			staff.m_customData["ChaosPolymorph_EmberForm"] = text;
			if (((Humanoid)p).GetCurrentWeapon() == staff)
			{
				PublishEquippedForm(p, staff);
			}
			((Character)p).Message((MessageType)2, "The staff now holds an ember " + Pretty(text) + ".", 0, (Sprite)null, false);
			if (Cfg.HenCastEffect.Value)
			{
				EmberFx.Burst(((Component)p).transform.position + Vector3.up * 1.2f, 0.8f, ring: false);
			}
			if (Cfg.HenPlaySound.Value)
			{
				Fx.CreatureSound(text, ((Component)p).transform.position, isCaster: true);
			}
			Caster.Debug("slotted " + text + " into the Ember staff");
			return true;
		}

		public static void PublishEquippedForm(Player p, ItemData staff)
		{
			ZNetView val = Access.NView((Character)(object)p);
			if ((Object)(object)val != (Object)null && val.IsValid() && val.IsOwner())
			{
				val.GetZDO().Set(PlayerFormKey, FormOf(staff));
			}
		}

		public static string Pretty(string prefab)
		{
			if (prefab == "Player")
			{
				return "Viking";
			}
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null);
			Character val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Character>() : null);
			if ((Object)(object)val2 != (Object)null && !string.IsNullOrEmpty(val2.m_name) && Localization.instance != null)
			{
				return Localization.instance.Localize(val2.m_name);
			}
			return prefab;
		}

		private static void Set(object o, string field, object value)
		{
			AccessTools.Field(o.GetType(), field)?.SetValue(o, value);
		}

		private static void SetEnum(object o, string field, string name)
		{
			FieldInfo fieldInfo = AccessTools.Field(o.GetType(), field);
			if (!(fieldInfo == null) && fieldInfo.FieldType.IsEnum && Enum.GetNames(fieldInfo.FieldType).Contains(name))
			{
				fieldInfo.SetValue(o, Enum.Parse(fieldInfo.FieldType, name));
			}
		}
	}
	internal static class HenModel
	{
		public static Material EmberTemplate;

		private static GameObject _iconRoot;

		private static GameObject _inactiveHolder;

		public static Material Tint(Material src, StaffStyle style)
		{
			if ((Object)(object)src == (Object)null)
			{
				return null;
			}
			if (!style.Cache.TryGetValue(src, out var value) || (Object)(object)value == (Object)null)
			{
				value = (style.Cache[src] = MakeTinted(src, style));
			}
			return value;
		}

		public static void Apply(GameObject itemPrefab, SharedData shared, StaffStyle style, Action<Sprite> onIcon = null)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: 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_05c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e9: Expected O, but got Unknown
			//IL_02cb: 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_02d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			//IL_030b: Unknown result type (might be due to invalid IL or missing references)
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0316: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: 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_0492: Unknown result type (might be due to invalid IL or missing references)
			//IL_0496: Unknown result type (might be due to invalid IL or missing references)
			//IL_049b: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0500: Unknown result type (might be due to invalid IL or missing references)
			//IL_0502: Unknown result type (might be due to invalid IL or missing references)
			//IL_0507: Unknown result type (might be due to invalid IL or missing references)
			//IL_055a: Unknown result type (might be due to invalid IL or missing references)
			//IL_055c: Unknown result type (might be due to invalid IL or missing references)
			if ((int)SystemInfo.graphicsDeviceType == 4)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder(style.Label + " staff model:\n");
			EmberFx.FindMaterial(itemPrefab);
			GameObject prefab = PrefabManager.Instance.GetPrefab("Hen");
			EmberTemplate = (((Object)(object)prefab != (Object)null) ? (from r in prefab.GetComponentsInChildren<Renderer>(true)
				select r.sharedMaterial).FirstOrDefault((Func<Material, bool>)((Material m) => (Object)(object)m != (Object)null && m.HasProperty("_EmissionColor") && m.HasProperty("_Color"))) : null);
			if (Cfg.HenHideOriginalEffects.Value)
			{
				ParticleSystemRenderer[] componentsInChildren = itemPrefab.GetComponentsInChildren<ParticleSystemRenderer>(true);
				for (int num = 0; num < componentsInChildren.Length; num++)
				{
					((Renderer)componentsInChildren[num]).enabled = false;
				}
				Light[] componentsInChildren2 = itemPrefab.GetComponentsInChildren<Light>(true);
				for (int num = 0; num < componentsInChildren2.Length; num++)
				{
					((Behaviour)componentsInChildren2[num]).enabled = false;
				}
			}
			List<MeshFilter> list = (from f in itemPrefab.GetComponentsInChildren<MeshFilter>(true)
				where (Object)(object)f.sharedMesh != (Object)null && (Object)(object)((Component)f).GetComponent<MeshRenderer>() != (Object)null
				select f).ToList();
			if (list.Count == 0)
			{
				Plugin.Log.LogWarning((object)(style.Label + " staff base has no mesh renderers."));
				return;
			}
			float num2 = list.Max(delegate(MeshFilter f)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: 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)
				Bounds bounds2 = f.sharedMesh.bounds;
				return MaxC(((Bounds)(ref bounds2)).size);
			});
			Dictionary<Mesh, Mesh> dictionary = new Dictionary<Mesh, Mesh>();
			EmberTopper emberTopper = null;
			MeshFilter shaft = null;
			Vector3 headDir = Vector3.up;
			int axis = 1;
			bool headPos = true;
			float top = 0f;
			float foot = 0f;
			foreach (MeshFilter item in list)
			{
				Mesh sharedMesh = item.sharedMesh;
				Bounds bounds = sharedMesh.bounds;
				MeshRenderer component = ((Component)item).GetComponent<MeshRenderer>();
				stringBuilder.AppendLine($"  base part '{PathOf(((Component)item).transform, itemPrefab.transform)}' mesh={((Object)sharedMesh).name} size={((Bounds)(ref bounds)).size} readable={sharedMesh.isReadable}");
				if (MaxC(((Bounds)(ref bounds)).size) < num2 * 0.6f)
				{
					if (Cfg.HenTrimHead.Value)
					{
						((Renderer)component).enabled = false;
					}
					continue;
				}
				int num3 = ((!(((Bounds)(ref bounds)).size.x >= ((Bounds)(ref bounds)).size.y) || !(((Bounds)(ref bounds)).size.x >= ((Bounds)(ref bounds)).size.z)) ? ((((Bounds)(ref bounds)).size.y >= ((Bounds)(ref bounds)).size.z) ? 1 : 2) : 0);
				Vector3 val = (Vector3)(num3 switch
				{
					1 => Vector3.up, 
					0 => Vector3.right, 
					_ => Vector3.forward, 
				});
				Vector3 val2 = ((Bounds)(ref bounds)).max;
				float num4 = Mathf.Abs(((Vector3)(ref val2))[num3]);
				val2 = ((Bounds)(ref bounds)).min;
				bool flag = num4 >= Mathf.Abs(((Vector3)(ref val2))[num3]);
				Vector3 val3 = (flag ? val : (-val));
				float num5;
				if (!flag)
				{
					val2 = ((Bounds)(ref bounds)).min;
					num5 = ((Vector3)(ref val2))[num3];
				}
				else
				{
					val2 = ((Bounds)(ref bounds)).max;
					num5 = ((Vector3)(ref val2))[num3];
				}
				float num6 = num5;
				float num7;
				if (!flag)
				{
					val2 = ((Bounds)(ref bounds)).max;
					num7 = ((Vector3)(ref val2))[num3];
				}
				else
				{
					val2 = ((Bounds)(ref bounds)).min;
					num7 = ((Vector3)(ref val2))[num3];
				}
				float num8 = num7;
				val2 = ((Bounds)(ref bounds)).size;
				float num9 = ((Vector3)(ref val2))[num3];
				float num10 = num6;
				if (Cfg.HenTrimHead.Value && Cfg.HenTrimFraction.Value > 0f)
				{
					float num11 = num6 - (num6 - num8) * Cfg.HenTrimFraction.Value;
					if (!dictionary.TryGetValue(sharedMesh, out var value))
					{
						string how;
						Mesh val4 = MeshReadback.ReadableCopy(sharedMesh, out how);
						stringBuilder.AppendLine("    mesh data: " + how);
						value = (dictionary[sharedMesh] = (((Object)(object)val4 != (Object)null) ? TrimMesh(val4, num3, num11, flag) : null));
					}
					if ((Object)(object)value != (Object)null)
					{
						item.sharedMesh = value;
						stringBuilder.AppendLine($"    trimmed the head ({Cfg.HenTrimFraction.Value:P0})");
					}
					else
					{
						item.sharedMesh = PlainShaft(num3, num8, num11, num9 * Cfg.HenShaftThickness.Value);
						((Renderer)component).sharedMaterials = (Material[])(object)new Material[1] { WoodMaterial(((Renderer)component).sharedMaterial) };
						stringBuilder.AppendLine("    using a plain wooden shaft instead");
					}
					num10 = num11;
				}
				Vector3 localPosition = val * num10 + val3 * Cfg.HenHeightOffset.Value * num9;
				GameObject val6 = new GameObject("EmberTopper");
				val6.transform.SetParent(((Component)item).transform, false);
				val6.transform.localPosition = localPosition;
				val6.transform.localRotation = Quaternion.AngleAxis(Cfg.HenTwist.Value, val3) * Quaternion.FromToRotation(Vector3.up, val3);
				EmberTopper emberTopper2 = val6.AddComponent<EmberTopper>();
				emberTopper2.StyleId = style.Id;
				emberTopper2.TargetSize = num9 * style.Size();
				emberTopper2.Build(style.DefaultForm());
				if ((Object)(object)emberTopper == (Object)null)
				{
					emberTopper = emberTopper2;
					headDir = val3;
					shaft = item;
					axis = num3;
					headPos = flag;
					top = num10;
					foot = num8;
				}
			}
			if (Cfg.HenRenderIcon.Value && (Object)(object)emberTopper != (Object)null)
			{
				GameObject val7 = BuildIconModel(emberTopper, shaft, headDir, axis, headPos, top, foot, stringBuilder);
				RenderManager.Instance.EnqueueRender(new RenderRequest(val7)
				{
					Rotation = Quaternion.identity,
					UseCache = false
				}, (Action<Sprite>)delegate(Sprite sprite)
				{
					if ((Object)(object)sprite != (Object)null)
					{
						shared.m_icons = (Sprite[])(object)new Sprite[1] { sprite };
						onIcon?.Invoke(sprite);
					}
				});
			}
			if (Cfg.Verbose.Value)
			{
				Plugin.Log.LogInfo((object)stringBuilder.ToString());
			}
		}

		private static GameObject BuildIconModel(EmberTopper topper, MeshFilter shaft, Vector3 headDir, int axis, bool headPos, float top, float foot, StringBuilder log)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: 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)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due