Decompiled source of Herobrian spells v1.0.0

BepInEx\plugins\Herobrian_spells\Herobrian_spells.dll

Decompiled 15 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FMODUnity;
using HarmonyLib;
using Mirror;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using YAPYAP;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("YapyapFireBall")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("YapyapFireBall")]
[assembly: AssemblyTitle("YapyapFireBall")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace YapyapFireBallMod;

internal enum ControlActionKind
{
	HoldInput,
	Closer,
	Raise,
	SlamDown,
	Throw
}
internal enum ControlDirection
{
	Forward,
	Back,
	Left,
	Right,
	Up,
	Down,
	UpLeft,
	UpRight,
	DownLeft,
	DownRight,
	ForwardLeft,
	ForwardRight,
	BackLeft,
	BackRight
}
internal sealed class ControlActionDefinition
{
	internal string Key { get; }

	internal string[] Phrase { get; }

	internal ControlActionKind Kind { get; }

	internal ControlDirection Direction { get; }

	internal ControlActionDefinition(string key, string[] phrase, ControlActionKind kind, ControlDirection direction = ControlDirection.Forward)
	{
		Key = key;
		Phrase = phrase;
		Kind = kind;
		Direction = direction;
	}
}
internal static class ControlActionCatalog
{
	internal static readonly ControlActionDefinition[] VoiceActions = new ControlActionDefinition[19]
	{
		new ControlActionDefinition("MOD_CONTROL_CLOSER", new string[1] { "CLOSER" }, ControlActionKind.Closer),
		new ControlActionDefinition("MOD_CONTROL_RAISE", new string[1] { "RAISE" }, ControlActionKind.Raise),
		new ControlActionDefinition("MOD_CONTROL_SLAM_DOWN", new string[2] { "SLAM", "DOWN" }, ControlActionKind.SlamDown),
		Throw("FORWARD", ControlDirection.Forward),
		Throw("BACK", ControlDirection.Back),
		Throw("LEFT", ControlDirection.Left),
		Throw("RIGHT", ControlDirection.Right),
		Throw("UP", ControlDirection.Up),
		Throw("DOWN", ControlDirection.Down),
		Throw("UP_LEFT", ControlDirection.UpLeft, "UP", "LEFT"),
		Throw("UP_RIGHT", ControlDirection.UpRight, "UP", "RIGHT"),
		Throw("DOWN_LEFT", ControlDirection.DownLeft, "DOWN", "LEFT"),
		Throw("DOWN_RIGHT", ControlDirection.DownRight, "DOWN", "RIGHT"),
		Throw("BOTTOM_LEFT", ControlDirection.DownLeft, "BOTTOM", "LEFT"),
		Throw("BOTTOM_RIGHT", ControlDirection.DownRight, "BOTTOM", "RIGHT"),
		Throw("FORWARD_LEFT", ControlDirection.ForwardLeft, "FORWARD", "LEFT"),
		Throw("FORWARD_RIGHT", ControlDirection.ForwardRight, "FORWARD", "RIGHT"),
		Throw("BACK_LEFT", ControlDirection.BackLeft, "BACK", "LEFT"),
		Throw("BACK_RIGHT", ControlDirection.BackRight, "BACK", "RIGHT")
	};

	internal static IEnumerable<KeyValuePair<string, string[]>> GetVoiceCommands()
	{
		ControlActionDefinition[] voiceActions = VoiceActions;
		foreach (ControlActionDefinition controlActionDefinition in voiceActions)
		{
			yield return new KeyValuePair<string, string[]>(controlActionDefinition.Key, controlActionDefinition.Phrase);
		}
	}

	private static ControlActionDefinition Throw(string keySuffix, ControlDirection direction, params string[] directionWords)
	{
		string[] array = ((directionWords.Length != 0) ? directionWords : new string[1] { keySuffix });
		string[] array2 = new string[array.Length + 1];
		array2[0] = "THROW";
		Array.Copy(array, 0, array2, 1, array.Length);
		return new ControlActionDefinition("MOD_CONTROL_THROW_" + keySuffix, array2, ControlActionKind.Throw, direction);
	}
}
public sealed class ControlActionSpell : Spell
{
	[SerializeField]
	private ControlActionKind actionKind;

	[SerializeField]
	private ControlDirection direction;

	[SerializeField]
	private bool holdInputValue;

	internal ControlActionKind ActionKind => actionKind;

	internal ControlDirection Direction => direction;

	internal bool HoldInputValue => holdInputValue;

	internal bool IsHoldInputAction => ActionKind == ControlActionKind.HoldInput;

	internal void Configure(SpellData data, ControlActionKind actionKind, ControlDirection direction, bool holdInputValue = false)
	{
		base.spellData = data;
		this.actionKind = actionKind;
		this.direction = direction;
		this.holdInputValue = holdInputValue;
		base.OverrideSecret = true;
	}

	public override bool CanCast()
	{
		return false;
	}
}
public sealed class ControlInputRelay : MonoBehaviour
{
	private static readonly FieldInfo AllSpellsField = AccessTools.Field(typeof(VoiceSpell), "allSpells");

	private static readonly MethodInfo CastByIndexMethod = AccessTools.Method(typeof(VoiceSpell), "CmdCastSpellByIndex", (Type[])null, (Type[])null);

	[SerializeField]
	private VoiceSpell voiceSpell;

	private bool inputInitialized;

	private bool lastRPressed;

	private float nextSendRetry;

	internal bool ServerHoldActive { get; private set; }

	internal void Configure(VoiceSpell ownerVoiceSpell)
	{
		voiceSpell = ownerVoiceSpell;
	}

	internal void SetServerHold(bool active)
	{
		ServerHoldActive = active;
		if ((Object)(object)voiceSpell != (Object)null && voiceSpell.NetworkcurrentSpell is ControlSpell controlSpell)
		{
			controlSpell.SetHoldInput(active);
		}
	}

	private void Update()
	{
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)voiceSpell == (Object)null || (Object)(object)((Spell)voiceSpell).CasterIdentity == (Object)null || !((Spell)voiceSpell).CasterIdentity.isLocalPlayer)
		{
			inputInitialized = false;
			lastRPressed = false;
			return;
		}
		bool flag = Keyboard.current != null && ((ButtonControl)Keyboard.current[Plugin.ControlHoldKey]).isPressed;
		if ((!inputInitialized || flag != lastRPressed) && Time.unscaledTime >= nextSendRetry)
		{
			if (SendHoldState(flag))
			{
				inputInitialized = true;
				lastRPressed = flag;
			}
			else
			{
				nextSendRetry = Time.unscaledTime + 0.25f;
			}
		}
	}

	private bool SendHoldState(bool pressed)
	{
		if (!(AllSpellsField.GetValue(voiceSpell) is List<Spell> list))
		{
			return false;
		}
		int num = list.FindIndex((Spell spell) => spell is ControlActionSpell controlActionSpell && (Object)(object)((Component)controlActionSpell).gameObject == (Object)(object)((Component)voiceSpell).gameObject && controlActionSpell.IsHoldInputAction && controlActionSpell.HoldInputValue == pressed);
		if (num < 0)
		{
			return false;
		}
		try
		{
			CastByIndexMethod.Invoke(voiceSpell, new object[2] { num, false });
			return true;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Control R input relay failed: " + ex.GetBaseException().Message));
			return false;
		}
	}
}
internal static class ControlBootstrap
{
	private static readonly FieldInfo VoiceSpellsField = AccessTools.Field(typeof(VoiceSpell), "spells");

	private static readonly FieldInfo AllSpellsField = AccessTools.Field(typeof(VoiceSpell), "allSpells");

	private static readonly FieldInfo NetworkAssetIdField = AccessTools.Field(typeof(NetworkIdentity), "_assetId");

	private static readonly FieldInfo NetworkHasSpawnedField = AccessTools.Field(typeof(NetworkIdentity), "hasSpawned");

	private static readonly FieldInfo SpellTagField = AccessTools.Field(typeof(BaseProjectile), "spellTag");

	private static readonly FieldInfo PalmField = AccessTools.Field(typeof(ProjectileHand), "palmTransform");

	private static readonly FieldInfo RotationSpeedField = AccessTools.Field(typeof(ProjectileHand), "rotationSpeed");

	private static GameObject networkHandPrefab;

	private static SpellData controlSpellData;

	private static readonly Dictionary<string, SpellData> ActionSpellData = new Dictionary<string, SpellData>(StringComparer.Ordinal);

	internal static void Install(YapNetworkManager manager)
	{
		if ((Object)(object)manager == (Object)null)
		{
			return;
		}
		GameObject val = FindPrefab(manager, "PropWandOfMinorArcanes");
		GameObject val2 = FindPrefab(manager, "SpectralHand");
		if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
		{
			Plugin.Log.LogError((object)"Control was not installed: PropWandOfMinorArcanes or SpectralHand was not found.");
			return;
		}
		NetworkIdentity val3 = default(NetworkIdentity);
		if (((NetworkManager)manager).spawnPrefabs.Any((GameObject prefab) => (Object)(object)prefab != (Object)null && prefab.TryGetComponent<NetworkIdentity>(ref val3) && val3.assetId == 3229223441u))
		{
			Plugin.Log.LogError((object)$"Control hand asset ID collision: {3229223441u}. Control will not install.");
			return;
		}
		VoiceSpell component = val.GetComponent<VoiceSpell>();
		GrabSpell component2 = val.GetComponent<GrabSpell>();
		ProjectileHand component3 = val2.GetComponent<ProjectileHand>();
		if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || (Object)(object)component3 == (Object)null)
		{
			Plugin.Log.LogError((object)"Control was not installed: VoiceSpell, GrabSpell, or ProjectileHand is missing.");
			return;
		}
		EnsureRuntimeData(component2);
		EnsureHandPrefab(val2, component3);
		EnsureSpellsOnWand(val, component);
	}

	private static GameObject FindPrefab(YapNetworkManager manager, string name)
	{
		return ((IEnumerable<GameObject>)((NetworkManager)manager).spawnPrefabs).FirstOrDefault((Func<GameObject, bool>)((GameObject prefab) => (Object)(object)prefab != (Object)null && string.Equals(((Object)prefab).name, name, StringComparison.Ordinal)));
	}

	private static void EnsureRuntimeData(GrabSpell grabSpell)
	{
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)controlSpellData == (Object)null)
		{
			controlSpellData = Object.Instantiate<SpellData>(((Spell)grabSpell).SpellData);
			((Object)controlSpellData).name = "SD_ARC_CONTROL_MOD";
			controlSpellData.spellName = "CONTROL";
			controlSpellData.voiceCommandKey = "MOD_SPELL_ARC_CONTROL";
			controlSpellData.cooldown = 2f;
			controlSpellData.charges = 3;
			controlSpellData.chargePerCast = 1;
			controlSpellData.chargeCostPerSecondCast = 0;
			controlSpellData.chargeRegenInterval = 6f;
			controlSpellData.chargesRegenPerInterval = 1;
			controlSpellData.chargeRegenDelay = 0.5f;
			controlSpellData.spawnLocation = (SpawnLocation)0;
			controlSpellData.spawnOffset = Vector3.zero;
			controlSpellData.applyLookTransform = true;
			controlSpellData.spawnDelay = 0f;
		}
	}

	private static void EnsureHandPrefab(GameObject vanillaHandPrefab, ProjectileHand vanillaHand)
	{
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)networkHandPrefab != (Object)null)
		{
			controlSpellData.objectToSpawnPrefab = networkHandPrefab;
			return;
		}
		bool activeSelf = vanillaHandPrefab.activeSelf;
		try
		{
			vanillaHandPrefab.SetActive(false);
			networkHandPrefab = Object.Instantiate<GameObject>(vanillaHandPrefab);
		}
		finally
		{
			vanillaHandPrefab.SetActive(activeSelf);
		}
		((Object)networkHandPrefab).name = "ControlSpectralHandMod";
		networkHandPrefab.SetActive(false);
		Object.DontDestroyOnLoad((Object)(object)networkHandPrefab);
		NetworkIdentity component = networkHandPrefab.GetComponent<NetworkIdentity>();
		if ((Object)(object)component == (Object)null)
		{
			throw new InvalidOperationException("The cloned SpectralHand has no NetworkIdentity.");
		}
		NetworkAssetIdField.SetValue(component, 3229223441u);
		NetworkHasSpawnedField.SetValue(component, false);
		component.sceneId = 0uL;
		NetworkBehaviour[] components = networkHandPrefab.GetComponents<NetworkBehaviour>();
		foreach (NetworkBehaviour val in components)
		{
			if (((object)val).GetType().Name.IndexOf("NetworkTransform", StringComparison.Ordinal) >= 0)
			{
				val.syncDirection = (SyncDirection)0;
			}
		}
		ProjectileHand component2 = networkHandPrefab.GetComponent<ProjectileHand>();
		((Behaviour)component2).enabled = false;
		ControlProjectileHand obj = networkHandPrefab.GetComponent<ControlProjectileHand>() ?? networkHandPrefab.AddComponent<ControlProjectileHand>();
		ProjectileData projectileData = ((BaseProjectile)component2).ProjectileData;
		SpellData ownerSpellData = controlSpellData;
		object ownerSpellTag = (object)/*isinst with value type is only supported in some contexts*/;
		object? value = PalmField.GetValue(component2);
		obj.Configure(projectileData, ownerSpellData, (GameplayTag)ownerSpellTag, component2, (Transform)((value is Transform) ? value : null), (float)RotationSpeedField.GetValue(component2), Plugin.ControlHoldStrength);
		controlSpellData.objectToSpawnPrefab = networkHandPrefab;
		Plugin.Log.LogInfo((object)$"Prepared Control spectral hand with asset ID {3229223441u}.");
	}

	private static void EnsureSpellsOnWand(GameObject wandPrefab, VoiceSpell voiceSpell)
	{
		ControlSpell controlSpell = wandPrefab.GetComponent<ControlSpell>() ?? wandPrefab.AddComponent<ControlSpell>();
		controlSpell.Configure(controlSpellData);
		((Behaviour)controlSpell).enabled = true;
		(wandPrefab.GetComponent<ControlInputRelay>() ?? wandPrefab.AddComponent<ControlInputRelay>()).Configure(voiceSpell);
		List<Spell> list = ((VoiceSpellsField.GetValue(voiceSpell) as Spell[]) ?? Array.Empty<Spell>()).ToList();
		if (!list.Contains((Spell)(object)controlSpell))
		{
			list.Add((Spell)(object)controlSpell);
		}
		AddActionSpell(wandPrefab, list, controlSpellData, "MOD_CONTROL_R_PRESS", Array.Empty<string>(), ControlActionKind.HoldInput, ControlDirection.Forward, holdInputValue: true);
		AddActionSpell(wandPrefab, list, controlSpellData, "MOD_CONTROL_R_RELEASE", Array.Empty<string>(), ControlActionKind.HoldInput, ControlDirection.Forward);
		ControlActionDefinition[] voiceActions = ControlActionCatalog.VoiceActions;
		foreach (ControlActionDefinition controlActionDefinition in voiceActions)
		{
			AddActionSpell(wandPrefab, list, controlSpellData, controlActionDefinition.Key, controlActionDefinition.Phrase, controlActionDefinition.Kind, controlActionDefinition.Direction);
		}
		VoiceSpellsField.SetValue(voiceSpell, list.ToArray());
		Plugin.Log.LogInfo((object)($"Added Control, R input relay, and {ControlActionCatalog.VoiceActions.Length} " + $"voice actions to PropWandOfMinorArcanes ({list.Count} entries)."));
	}

	private static void AddActionSpell(GameObject wandPrefab, List<Spell> spells, SpellData template, string key, string[] phrase, ControlActionKind actionKind, ControlDirection direction, bool holdInputValue = false)
	{
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		ControlActionSpell controlActionSpell = wandPrefab.GetComponents<ControlActionSpell>().FirstOrDefault((ControlActionSpell action) => action.ActionKind == actionKind && action.Direction == direction && action.HoldInputValue == holdInputValue && (Object)(object)((Spell)action).SpellData != (Object)null && string.Equals(((Object)((Spell)action).SpellData).name, "SD_ARC_" + key, StringComparison.Ordinal));
		if ((Object)(object)controlActionSpell == (Object)null)
		{
			controlActionSpell = wandPrefab.AddComponent<ControlActionSpell>();
		}
		if (!ActionSpellData.TryGetValue(key, out var value))
		{
			value = Object.Instantiate<SpellData>(template);
			((Object)value).name = "SD_ARC_" + key;
			value.spellName = ((phrase.Length == 0) ? key : string.Join(" ", phrase));
			value.voiceCommandKey = ((phrase.Length == 0) ? string.Empty : key);
			value.cooldown = 0f;
			value.charges = -1;
			value.chargePerCast = 0;
			value.chargeCostPerSecondCast = 0;
			value.chargesRegenPerInterval = 0;
			value.objectToSpawnPrefab = null;
			value.castingAnimationType = (CastingAnimationType)0;
			value.castingLoopAnimationType = (CastingLoopAnimationType)0;
			ActionSpellData.Add(key, value);
		}
		controlActionSpell.Configure(value, actionKind, direction, holdInputValue);
		((Behaviour)controlActionSpell).enabled = true;
		if (!spells.Contains((Spell)(object)controlActionSpell))
		{
			spells.Add((Spell)(object)controlActionSpell);
		}
	}

	internal static void RegisterClientSpawnHandler()
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Expected O, but got Unknown
		//IL_0049: Expected O, but got Unknown
		if ((Object)(object)networkHandPrefab == (Object)null)
		{
			Plugin.Log.LogWarning((object)"Cannot register Control client spawn handler: hand prefab is not prepared.");
			return;
		}
		NetworkClient.UnregisterSpawnHandler(3229223441u);
		NetworkClient.RegisterSpawnHandler(3229223441u, new SpawnHandlerDelegate(SpawnClientHand), new UnSpawnDelegate(UnspawnClientHand));
		Plugin.Log.LogInfo((object)$"Registered Control client spawn handler for asset ID {3229223441u}.");
	}

	private static GameObject SpawnClientHand(SpawnMessage message)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		GameObject obj = Object.Instantiate<GameObject>(networkHandPrefab, message.position, message.rotation);
		obj.SetActive(true);
		return obj;
	}

	private static void UnspawnClientHand(GameObject instance)
	{
		if ((Object)(object)instance != (Object)null)
		{
			Object.Destroy((Object)(object)instance);
		}
	}

	internal static bool ShouldRunOriginalCastCommand(VoiceSpell voiceSpell, int index)
	{
		if (!(AllSpellsField.GetValue(voiceSpell) is List<Spell> list) || index < 0 || index >= list.Count || !(list[index] is ControlActionSpell controlActionSpell))
		{
			return true;
		}
		if (controlActionSpell.IsHoldInputAction)
		{
			((Component)voiceSpell).GetComponent<ControlInputRelay>()?.SetServerHold(controlActionSpell.HoldInputValue);
			Plugin.Log.LogDebug((object)$"Server received Control R state: {controlActionSpell.HoldInputValue}.");
			return false;
		}
		if (!(voiceSpell.NetworkcurrentSpell is ControlSpell controlSpell))
		{
			return false;
		}
		if (controlSpell.TryApplyAction(controlActionSpell.ActionKind, controlActionSpell.Direction))
		{
			voiceSpell.NetworkcurrentSpell = null;
			((Spell)voiceSpell).NetworkisCasting = false;
			voiceSpell.StopPrimingAnimation();
		}
		return false;
	}

	internal static bool ShouldRunOriginalStopCommand(VoiceSpell voiceSpell)
	{
		if (voiceSpell.NetworkcurrentSpell is ControlSpell)
		{
			Plugin.Log.LogDebug((object)"Ignored duplicate CONTROL voice-stop; R release owns Control cancellation.");
			return false;
		}
		return true;
	}
}
[HarmonyPatch(typeof(VoiceSpell), "UserCode_CmdCastSpellByIndex__Int32__Boolean")]
internal static class ControlThrowCommandPatch
{
	[HarmonyPrefix]
	private static bool Prefix(VoiceSpell __instance, int index)
	{
		return ControlBootstrap.ShouldRunOriginalCastCommand(__instance, index);
	}
}
[HarmonyPatch(typeof(VoiceSpell), "UserCode_CmdStopCurrentSpell")]
internal static class ControlDuplicateVoiceStopPatch
{
	[HarmonyPrefix]
	private static bool Prefix(VoiceSpell __instance)
	{
		return ControlBootstrap.ShouldRunOriginalStopCommand(__instance);
	}
}
[HarmonyPatch(typeof(NetworkPuppetWandProp), "SetSpellCasting")]
internal static class ControlMouseIsolationPatch
{
	[HarmonyPrefix]
	private static bool Prefix(NetworkPuppetWandProp __instance, bool wantsToCast)
	{
		if (NetworkServer.active)
		{
			Spell spell = __instance.Spell;
			VoiceSpell val = (VoiceSpell)(object)((spell is VoiceSpell) ? spell : null);
			if (val != null && val.NetworkcurrentSpell is ControlSpell)
			{
				__instance.NetworkisCastingSpell = wantsToCast;
				return false;
			}
		}
		return true;
	}
}
public sealed class ControlProjectileHand : BaseProjectile
{
	private enum ControlState
	{
		Flying,
		Captured,
		Returning,
		Holding,
		Ending
	}

	private static readonly MethodInfo ReachRpc = AccessTools.Method(typeof(ProjectileHand), "RpcPlayReachAnimation", (Type[])null, (Type[])null);

	private static readonly MethodInfo GrabRpc = AccessTools.Method(typeof(ProjectileHand), "RpcPlayGrabAnimation", (Type[])null, (Type[])null);

	private static readonly MethodInfo PullRpc = AccessTools.Method(typeof(ProjectileHand), "RpcPlayPullAnimation", (Type[])null, (Type[])null);

	private static readonly MethodInfo ObstacleRpc = AccessTools.Method(typeof(ProjectileHand), "RpcPlayHitObstacleAnim", (Type[])null, (Type[])null);

	[SerializeField]
	private ProjectileHand visualBridge;

	[SerializeField]
	private Transform palmTransform;

	[SerializeField]
	private float rotationSpeed = 180f;

	[SerializeField]
	private float captureInputWindow = 5f;

	[SerializeField]
	private float returnSpeed = 8f;

	[SerializeField]
	private float holdStrength = 8f;

	[SerializeField]
	private float itemHoldDistance = 0.5f;

	[SerializeField]
	private float playerHoldDistance = 1f;

	[SerializeField]
	private float enemyHoldDistance = 2.5f;

	private ControlState state;

	private Rigidbody grabbedBody;

	private Pawn grabbedPawn;

	private NpcBehaviour grabbedNpc;

	private SpringJoint grabJoint;

	private GameObject anchorPoint;

	private Collider[] handColliders;

	private Collider[] grabbedColliders;

	private Collider casterCapsule;

	private Vector3 localGrabPoint;

	private float captureDeadline;

	private float nextPawnCorrectionTime;

	private float holdDistanceAdjustment;

	private float raisedHeight;

	private float slamUntil;

	private bool wasThrown;

	private bool npcNavigationDisabled;

	private bool ignoringCasterCollision;

	public bool HasGrabbedTarget
	{
		get
		{
			if ((Object)(object)grabbedBody != (Object)null)
			{
				if (state != ControlState.Captured && state != ControlState.Returning)
				{
					return state == ControlState.Holding;
				}
				return true;
			}
			return false;
		}
	}

	internal Rigidbody GrabbedBody => grabbedBody;

	internal NetworkIdentity ControlCaster => base.Caster;

	internal void Configure(ProjectileData data, SpellData ownerSpellData, GameplayTag ownerSpellTag, ProjectileHand bridge, Transform palm, float rotateSpeed, float configuredHoldStrength)
	{
		base.projectileData = data;
		base.spellData = ownerSpellData;
		base.spellTag = ownerSpellTag;
		visualBridge = bridge;
		palmTransform = palm;
		rotationSpeed = rotateSpeed;
		holdStrength = configuredHoldStrength;
		base.ttl = 5f;
		base.ignoreCaster = true;
		base.delayProcessObstacles = true;
	}

	public override void Initialize(NetworkIdentity caster, Spell ownerSpell)
	{
		((BaseProjectile)this).Initialize(caster, ownerSpell);
		state = ControlState.Flying;
		handColliders = ((Component)this).GetComponents<Collider>();
	}

	public override void Fire(Vector3 direction)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		((BaseProjectile)this).Fire(direction);
		if (((NetworkBehaviour)this).isServer)
		{
			state = ControlState.Flying;
			InvokeVisualRpc(ReachRpc);
		}
	}

	protected override void FixedUpdate()
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: 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_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: 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)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_0163: 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_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_0194: 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_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		if (!((NetworkBehaviour)this).isServer || state == ControlState.Ending || !base.hasBeenFired)
		{
			return;
		}
		if (state == ControlState.Flying)
		{
			((BaseProjectile)this).FixedUpdate();
			if (((BaseProjectile)this).Direction != Vector3.zero)
			{
				((Component)this).transform.rotation = Quaternion.RotateTowards(((Component)this).transform.rotation, Quaternion.LookRotation(((BaseProjectile)this).Direction), rotationSpeed * Time.fixedDeltaTime);
			}
			return;
		}
		if ((Object)(object)grabbedBody == (Object)null)
		{
			DropAndDestroy();
			return;
		}
		if (state == ControlState.Captured)
		{
			((Component)this).transform.position = ((Component)grabbedBody).transform.TransformPoint(localGrabPoint);
			if (IsHoldInputActive())
			{
				BeginReturn();
			}
			else if (Time.time >= captureDeadline)
			{
				DropAndDestroy();
			}
			return;
		}
		if (!IsHoldInputActive())
		{
			DropAndDestroy();
			return;
		}
		Vector3 aimDirection;
		Vector3 safePoint = GetSafePoint(out aimDirection);
		float num = ((Time.time < slamUntil) ? (returnSpeed * 2f) : returnSpeed);
		float num2 = Vector3.Distance(((Component)this).transform.position, safePoint);
		if (Time.time >= slamUntil)
		{
			num = Mathf.Lerp(1f, num, Mathf.Clamp01(num2 / 1.5f));
		}
		((Component)this).transform.position = Vector3.MoveTowards(((Component)this).transform.position, safePoint, num * Time.fixedDeltaTime);
		if (aimDirection != Vector3.zero)
		{
			((Component)this).transform.rotation = Quaternion.LookRotation(aimDirection);
		}
		PullTargetToward(((Component)this).transform.position);
		bool flag = BrakeTargetAtSafePoint(safePoint, aimDirection);
		state = ((Vector3.Distance(((Component)this).transform.position, safePoint) <= 0.05f && flag) ? ControlState.Holding : ControlState.Returning);
	}

	protected override bool ShouldDestroyOnTargetHit()
	{
		return false;
	}

	protected override bool ShouldDestroyOnWallHit()
	{
		return false;
	}

	protected override void OnTriggerEnter(Collider other)
	{
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		if (!((NetworkBehaviour)this).isServer || state != ControlState.Flying || !base.hasBeenFired || (Object)(object)base.projectileData == (Object)null || (other.isTrigger && !((Component)other).CompareTag("TriggerPhysical")) || ((Component)other).gameObject.CompareTag("IgnoreCollision") || ((Component)other).gameObject.CompareTag("EntityBlocker") || base.hasHit || Time.time - base.spawnTime < base.projectileData.spawnInvulnerability || ((Object)(object)base.Caster != (Object)null && ((Component)other).transform.IsChildOf(((Component)base.Caster).transform)))
		{
			return;
		}
		NetworkIdentity val = ((Component)other).GetComponent<NetworkIdentity>() ?? ((Component)other).GetComponentInParent<NetworkIdentity>();
		Rigidbody attachedRigidbody = other.attachedRigidbody;
		int num = 1 << ((Component)other).gameObject.layer;
		if ((((LayerMask)(ref base.projectileData.wallLayer)).value & num) != 0 || (Object)(object)attachedRigidbody == (Object)null)
		{
			if (base.delayProcessObstacles)
			{
				base.hitObstacleFlag = true;
				base.hitObstacleFlagCollider = other;
			}
			else
			{
				((BaseProjectile)this).OnWallHit(other);
			}
		}
		else if ((((LayerMask)(ref base.projectileData.targetLayer)).value & num) != 0)
		{
			((BaseProjectile)this).OnTargetHit(other, attachedRigidbody, val);
			if (state == ControlState.Captured && (Object)(object)base.OwnerSpell != (Object)null)
			{
				base.OwnerSpell.RpcPlayEffect((SpellEffectType)3, ((Component)this).transform.position, ((Component)this).transform.rotation);
			}
		}
	}

	protected override void OnWallHit(Collider wall)
	{
		if (((NetworkBehaviour)this).isServer && state == ControlState.Flying)
		{
			((BaseProjectile)this).OnWallHit(wall);
			state = ControlState.Ending;
			base.stopMoving = true;
			InvokeVisualRpc(ObstacleRpc);
			((MonoBehaviour)this).StartCoroutine(DestroyAfterDelay());
		}
	}

	protected override void OnTargetHit(Collider target, Rigidbody targetRigidbody, NetworkIdentity targetIdentity)
	{
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		if (!((NetworkBehaviour)this).isServer || state != ControlState.Flying || (Object)(object)targetRigidbody == (Object)null || (Object)(object)((Component)targetRigidbody).GetComponent<ProjectileHand>() != (Object)null || (Object)(object)((Component)targetRigidbody).GetComponent<ControlProjectileHand>() != (Object)null)
		{
			return;
		}
		UnstickTrigger val = default(UnstickTrigger);
		if (targetRigidbody.isKinematic && !((Component)targetRigidbody).TryGetComponent<UnstickTrigger>(ref val))
		{
			((BaseProjectile)this).OnWallHit(target);
			return;
		}
		grabbedBody = targetRigidbody;
		grabbedPawn = ((Component)targetRigidbody).GetComponent<Pawn>() ?? ((Component)targetRigidbody).GetComponentInParent<Pawn>() ?? (((Object)(object)targetIdentity == (Object)null) ? null : ((Component)targetIdentity).GetComponent<Pawn>());
		grabbedNpc = ((Component)targetRigidbody).GetComponent<NpcBehaviour>() ?? ((Component)targetRigidbody).GetComponentInParent<NpcBehaviour>() ?? (((Object)(object)targetIdentity == (Object)null) ? null : ((Component)targetIdentity).GetComponent<NpcBehaviour>());
		Transform val2 = (((Object)(object)palmTransform == (Object)null) ? ((Component)this).transform : palmTransform);
		Vector3 val3 = (target.isTrigger ? ((Component)target).transform.position : target.ClosestPoint(val2.position));
		localGrabPoint = ((Component)grabbedBody).transform.InverseTransformPoint(val3);
		grabbedColliders = ((Component)grabbedBody).GetComponentsInChildren<Collider>(true);
		captureDeadline = Time.time + captureInputWindow;
		base.stopMoving = true;
		state = ControlState.Captured;
		SetHandCollidersEnabled(enabled: false);
		if ((Object)(object)targetIdentity != (Object)null)
		{
			HandleTargetTags(targetIdentity);
		}
		InvokeVisualRpc(GrabRpc);
		Plugin.Log.LogDebug((object)("Control captured " + GetTargetKind() + "; waiting for R until " + $"{captureDeadline:0.00}."));
		if (IsHoldInputActive())
		{
			BeginReturn();
		}
	}

	private bool IsHoldInputActive()
	{
		if (base.OwnerSpell is ControlSpell controlSpell)
		{
			return controlSpell.HoldInputActive;
		}
		return false;
	}

	private void BeginReturn()
	{
		if (state == ControlState.Captured && !((Object)(object)grabbedBody == (Object)null))
		{
			UnstickTrigger val = default(UnstickTrigger);
			if (((Component)grabbedBody).TryGetComponent<UnstickTrigger>(ref val))
			{
				val.TryUnstick(base.Caster);
			}
			if ((Object)(object)grabbedPawn == (Object)null && (Object)(object)grabbedNpc == (Object)null)
			{
				CreateGrabJoint();
			}
			SetCasterCollisionIgnored(ignore: true);
			if ((Object)(object)grabbedNpc != (Object)null && (Object)(object)grabbedNpc.npcToggler != (Object)null && grabbedNpc.npcToggler.NetworknavigationEnabled)
			{
				grabbedNpc.npcToggler.SvDisableNavigation(false);
				npcNavigationDisabled = true;
			}
			state = ControlState.Returning;
			InvokeVisualRpc(PullRpc);
			Plugin.Log.LogDebug((object)("Control returning " + GetTargetKind() + " to " + $"{GetSafeHoldDistance():0.0}m."));
		}
	}

	private void PullTargetToward(Vector3 targetPoint)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: 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_004d: 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_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)grabbedBody == (Object)null)
		{
			return;
		}
		Vector3 val = targetPoint - grabbedBody.worldCenterOfMass;
		if ((Object)(object)grabbedPawn != (Object)null)
		{
			if (Time.time >= nextPawnCorrectionTime)
			{
				nextPawnCorrectionTime = Time.time + 0.1f;
				Vector3 val2 = Vector3.ClampMagnitude(val * 2.5f, returnSpeed * 0.5f) - grabbedBody.linearVelocity;
				grabbedPawn.SvAddExternalImpulse(Vector3.ClampMagnitude(val2 * 0.5f, holdStrength * 0.5f));
			}
		}
		else if ((Object)(object)grabbedNpc != (Object)null)
		{
			Vector3 val3 = Vector3.ClampMagnitude(val * 2.5f, returnSpeed * 0.5f);
			grabbedNpc.AddForce(val3 * Mathf.Max(1f, grabbedBody.mass));
		}
		else if ((Object)(object)grabJoint == (Object)null && !grabbedBody.isKinematic)
		{
			grabbedBody.AddForce(val * holdStrength, (ForceMode)5);
		}
	}

	private bool BrakeTargetAtSafePoint(Vector3 safePoint, Vector3 aimDirection)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: 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_00c0: 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)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_0182: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0205: Unknown result type (might be due to invalid IL or missing references)
		//IL_020a: Unknown result type (might be due to invalid IL or missing references)
		//IL_021a: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0233: Unknown result type (might be due to invalid IL or missing references)
		//IL_0238: Unknown result type (might be due to invalid IL or missing references)
		//IL_0257: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)grabbedBody == (Object)null)
		{
			return false;
		}
		Vector3 val = ((Component)grabbedBody).transform.TransformPoint(localGrabPoint);
		Vector3 val2 = safePoint - val;
		if ((Object)(object)grabbedPawn != (Object)null || (Object)(object)grabbedNpc != (Object)null)
		{
			return ((Vector3)(ref val2)).sqrMagnitude <= 0.0625f;
		}
		if (grabbedBody.isKinematic)
		{
			return ((Vector3)(ref val2)).sqrMagnitude <= 0.0144f;
		}
		Vector3 val3 = ((((Vector3)(ref aimDirection)).sqrMagnitude < 0.001f) ? ((Component)this).transform.forward : ((Vector3)(ref aimDirection)).normalized);
		float num = 0f - Vector3.Dot(val - safePoint, val3);
		bool num2 = num > 0f;
		if (num2)
		{
			Rigidbody obj = grabbedBody;
			obj.position += val3 * (num + 0.01f);
			val = ((Component)grabbedBody).transform.TransformPoint(localGrabPoint);
			val2 = safePoint - val;
		}
		float magnitude = ((Vector3)(ref val2)).magnitude;
		float num3 = ((state == ControlState.Holding) ? 0.75f : Mathf.Clamp(magnitude * 2.5f, 0.75f, returnSpeed * 0.75f));
		Vector3 val4 = Vector3.ClampMagnitude(val2 * 4f, num3);
		float num4 = Mathf.Lerp(30f, 12f, Mathf.Clamp01(magnitude / 2f));
		Vector3 val5 = Vector3.MoveTowards(grabbedBody.linearVelocity, val4, num4 * Time.fixedDeltaTime);
		float num5 = Vector3.Dot(val5, val3);
		if (num2 && num5 < 0f)
		{
			val5 -= val3 * num5;
		}
		else if (magnitude <= 0.35f)
		{
			float num6 = Mathf.Max(0.15f, magnitude * 2f);
			if (num5 < 0f - num6)
			{
				val5 += val3 * (0f - num6 - num5);
			}
		}
		grabbedBody.linearVelocity = val5;
		if (magnitude <= 0.35f)
		{
			grabbedBody.angularVelocity = Vector3.MoveTowards(grabbedBody.angularVelocity, Vector3.zero, 12f * Time.fixedDeltaTime);
		}
		int num7;
		if (magnitude <= 0.12f)
		{
			Vector3 linearVelocity = grabbedBody.linearVelocity;
			num7 = ((((Vector3)(ref linearVelocity)).sqrMagnitude <= 0.5625f) ? 1 : 0);
			if (num7 != 0)
			{
				grabbedBody.linearVelocity = Vector3.zero;
			}
		}
		else
		{
			num7 = 0;
		}
		return (byte)num7 != 0;
	}

	private Vector3 GetSafePoint(out Vector3 aimDirection)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: 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_005b: 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_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)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_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)
		Pawn val = (((Object)(object)base.Caster == (Object)null) ? null : ((Component)base.Caster).GetComponent<Pawn>());
		if ((Object)(object)val != (Object)null)
		{
			aimDirection = val.Blackboard.AimDirection;
			if (aimDirection == Vector3.zero)
			{
				aimDirection = ((Component)val).transform.forward;
			}
			return val.LookOrigin + ((Vector3)(ref aimDirection)).normalized * GetSafeHoldDistance() + Vector3.up * GetVerticalOffset();
		}
		aimDirection = (((Object)(object)base.Caster == (Object)null) ? ((Component)this).transform.forward : ((Component)base.Caster).transform.forward);
		return (((Object)(object)base.Caster == (Object)null) ? ((Component)this).transform.position : ((Component)base.Caster).transform.position) + ((Vector3)(ref aimDirection)).normalized * GetSafeHoldDistance() + Vector3.up * GetVerticalOffset();
	}

	private float GetSafeHoldDistance()
	{
		float num = (((Object)(object)grabbedNpc != (Object)null) ? enemyHoldDistance : ((!((Object)(object)grabbedPawn != (Object)null)) ? itemHoldDistance : playerHoldDistance));
		return Mathf.Max(GetMinimumHoldDistance(), num + holdDistanceAdjustment);
	}

	private float GetMinimumHoldDistance()
	{
		if ((Object)(object)grabbedNpc != (Object)null)
		{
			return 1f;
		}
		if ((Object)(object)grabbedPawn != (Object)null)
		{
			return 0.5f;
		}
		return 0.25f;
	}

	private float GetVerticalOffset()
	{
		return raisedHeight + ((Time.time < slamUntil) ? (-3f) : 0f);
	}

	private string GetTargetKind()
	{
		if ((Object)(object)grabbedNpc != (Object)null)
		{
			return "enemy";
		}
		if ((Object)(object)grabbedPawn != (Object)null)
		{
			return "player";
		}
		return "item";
	}

	private void CreateGrabJoint()
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Expected O, but got Unknown
		//IL_00ac: 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)
		if (!((Object)(object)grabbedBody == (Object)null) && !grabbedBody.isKinematic && !((Object)(object)grabJoint != (Object)null))
		{
			Transform val = (((Object)(object)palmTransform == (Object)null) ? ((Component)this).transform : palmTransform);
			anchorPoint = new GameObject("ControlAnchor");
			anchorPoint.transform.SetParent(val, false);
			Rigidbody val2 = anchorPoint.AddComponent<Rigidbody>();
			val2.isKinematic = true;
			val2.useGravity = false;
			grabJoint = ((Component)grabbedBody).gameObject.AddComponent<SpringJoint>();
			((Joint)grabJoint).connectedBody = val2;
			((Joint)grabJoint).anchor = localGrabPoint;
			((Joint)grabJoint).connectedAnchor = Vector3.zero;
			grabJoint.spring = 300f;
			grabJoint.damper = Mathf.Clamp(2f * Mathf.Sqrt(grabJoint.spring * grabbedBody.mass), 35f, 120f);
			grabJoint.maxDistance = 0f;
			((Joint)grabJoint).autoConfigureConnectedAnchor = false;
		}
	}

	private void SetCasterCollisionIgnored(bool ignore)
	{
		if (ignoringCasterCollision == ignore)
		{
			return;
		}
		if ((Object)(object)casterCapsule == (Object)null && (Object)(object)base.Caster != (Object)null)
		{
			Pawn component = ((Component)base.Caster).GetComponent<Pawn>();
			casterCapsule = (Collider)(object)(((Object)(object)component == (Object)null) ? null : component.CapsuleCollider);
		}
		if ((Object)(object)casterCapsule == (Object)null || grabbedColliders == null)
		{
			return;
		}
		Collider[] array = grabbedColliders;
		foreach (Collider val in array)
		{
			if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)casterCapsule)
			{
				Physics.IgnoreCollision(casterCapsule, val, ignore);
			}
		}
		ignoringCasterCollision = ignore;
	}

	private void DestroyGrabJoint()
	{
		if ((Object)(object)grabJoint != (Object)null)
		{
			Object.Destroy((Object)(object)grabJoint);
			grabJoint = null;
		}
		if ((Object)(object)anchorPoint != (Object)null)
		{
			Object.Destroy((Object)(object)anchorPoint);
			anchorPoint = null;
		}
	}

	private void SetHandCollidersEnabled(bool enabled)
	{
		if (handColliders == null)
		{
			return;
		}
		Collider[] array = handColliders;
		foreach (Collider val in array)
		{
			if ((Object)(object)val != (Object)null)
			{
				val.enabled = enabled;
			}
		}
	}

	[Server]
	public void DropAndDestroy()
	{
		if (NetworkServer.active && state != ControlState.Ending)
		{
			state = ControlState.Ending;
			DestroyGrabJoint();
			ClearTargetReferences();
			NetworkServer.Destroy(((Component)this).gameObject);
		}
	}

	[Server]
	internal void MoveCloser()
	{
		if (NetworkServer.active && HasGrabbedTarget)
		{
			holdDistanceAdjustment -= 0.5f;
			state = ControlState.Returning;
			Plugin.Log.LogDebug((object)$"Control CLOSER: {GetSafeHoldDistance():0.00}m.");
		}
	}

	[Server]
	internal void Raise()
	{
		if (NetworkServer.active && HasGrabbedTarget)
		{
			raisedHeight = Mathf.Min(4f, raisedHeight + 1f);
			state = ControlState.Returning;
			Plugin.Log.LogDebug((object)$"Control RAISE: vertical offset {raisedHeight:0.00}m.");
		}
	}

	[Server]
	internal void SlamDown(float force)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		if (NetworkServer.active && HasGrabbedTarget)
		{
			slamUntil = Time.time + 0.5f;
			state = ControlState.Returning;
			ApplyActionImpulse(Vector3.down, force);
			Plugin.Log.LogDebug((object)"Control SLAM DOWN.");
		}
	}

	[Server]
	internal void ThrowAndDestroy(float force, ControlDirection controlDirection)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: 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_0069: 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_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		if (NetworkServer.active && !((Object)(object)grabbedBody == (Object)null))
		{
			Vector3 throwDirection = GetThrowDirection(controlDirection);
			Rigidbody val = grabbedBody;
			Pawn val2 = grabbedPawn;
			NpcBehaviour val3 = grabbedNpc;
			wasThrown = true;
			state = ControlState.Ending;
			DestroyGrabJoint();
			if ((Object)(object)val2 != (Object)null)
			{
				val2.SvAddExternalImpulse(throwDirection * force);
			}
			else if ((Object)(object)val3 != (Object)null)
			{
				val3.AddForce(throwDirection * (force * 2f));
			}
			else if (!val.isKinematic)
			{
				val.AddForce(throwDirection * force, (ForceMode)2);
			}
			ClearTargetReferences();
			NetworkServer.Destroy(((Component)this).gameObject);
		}
	}

	private void ApplyActionImpulse(Vector3 direction, float force)
	{
		//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_001d: 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_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		direction = ((Vector3)(ref direction)).normalized;
		if ((Object)(object)grabbedPawn != (Object)null)
		{
			grabbedPawn.SvAddExternalImpulse(direction * force);
		}
		else if ((Object)(object)grabbedNpc != (Object)null)
		{
			grabbedNpc.AddForce(direction * (force * 2f));
		}
		else if ((Object)(object)grabbedBody != (Object)null && !grabbedBody.isKinematic)
		{
			grabbedBody.AddForce(direction * force, (ForceMode)2);
		}
	}

	private Vector3 GetThrowDirection(ControlDirection direction)
	{
		//IL_0003: 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_000a: 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_0018: 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_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: 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_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: 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_0161: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: 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_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		GetSafePoint(out var aimDirection);
		aimDirection = ((aimDirection == Vector3.zero) ? ((Component)this).transform.forward : ((Vector3)(ref aimDirection)).normalized);
		Vector3 val = Vector3.Cross(Vector3.up, aimDirection);
		if (((Vector3)(ref val)).sqrMagnitude < 0.001f)
		{
			val = (((Object)(object)base.Caster == (Object)null) ? ((Component)this).transform.right : ((Component)base.Caster).transform.right);
		}
		((Vector3)(ref val)).Normalize();
		Vector3 val2;
		switch (direction)
		{
		case ControlDirection.Back:
			return -aimDirection;
		case ControlDirection.Left:
			return -val;
		case ControlDirection.Right:
			return val;
		case ControlDirection.Up:
			return Vector3.up;
		case ControlDirection.Down:
			return Vector3.down;
		case ControlDirection.UpLeft:
			val2 = Vector3.up - val;
			return ((Vector3)(ref val2)).normalized;
		case ControlDirection.UpRight:
			val2 = Vector3.up + val;
			return ((Vector3)(ref val2)).normalized;
		case ControlDirection.DownLeft:
			val2 = Vector3.down - val;
			return ((Vector3)(ref val2)).normalized;
		case ControlDirection.DownRight:
			val2 = Vector3.down + val;
			return ((Vector3)(ref val2)).normalized;
		case ControlDirection.ForwardLeft:
			val2 = aimDirection - val;
			return ((Vector3)(ref val2)).normalized;
		case ControlDirection.ForwardRight:
			val2 = aimDirection + val;
			return ((Vector3)(ref val2)).normalized;
		case ControlDirection.BackLeft:
			val2 = -aimDirection - val;
			return ((Vector3)(ref val2)).normalized;
		case ControlDirection.BackRight:
			val2 = -aimDirection + val;
			return ((Vector3)(ref val2)).normalized;
		default:
			return aimDirection;
		}
	}

	private void ClearTargetReferences()
	{
		SetCasterCollisionIgnored(ignore: false);
		grabbedBody = null;
		grabbedPawn = null;
		grabbedNpc = null;
		grabbedColliders = null;
		casterCapsule = null;
		npcNavigationDisabled = false;
	}

	private void HandleTargetTags(NetworkIdentity targetIdentity)
	{
		NpcBehaviour val = default(NpcBehaviour);
		if ((Object)(object)base.Caster != (Object)null && ((Component)targetIdentity).TryGetComponent<NpcBehaviour>(ref val))
		{
			val.OnAggro(base.Caster);
		}
		TaggableObject val2 = default(TaggableObject);
		if (((Component)targetIdentity).TryGetComponent<TaggableObject>(ref val2))
		{
			val2.AddTag(TagReferences.MovedTag);
			if ((Object)(object)base.spellTag != (Object)null)
			{
				val2.AddTag(base.spellTag);
			}
		}
		ValuableObject val3 = default(ValuableObject);
		if ((Object)(object)base.Caster != (Object)null && ((Component)targetIdentity).TryGetComponent<ValuableObject>(ref val3))
		{
			val3.SetAndPropagateRecentInteractor(base.Caster.netId);
		}
	}

	private IEnumerator DestroyAfterDelay()
	{
		yield return (object)new WaitForSeconds(1f);
		if (NetworkServer.active)
		{
			NetworkServer.Destroy(((Component)this).gameObject);
		}
	}

	private void InvokeVisualRpc(MethodInfo method)
	{
		if ((Object)(object)visualBridge == (Object)null || method == null)
		{
			return;
		}
		try
		{
			method.Invoke(visualBridge, null);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Control hand visual RPC failed (" + method.Name + "): " + ex.GetBaseException().Message));
		}
	}

	private void OnDestroy()
	{
		SetCasterCollisionIgnored(ignore: false);
		DestroyGrabJoint();
		if (!wasThrown && npcNavigationDisabled)
		{
			npcNavigationDisabled = false;
		}
	}
}
public sealed class ControlSpell : SpawnableObjectSpell<ControlProjectileHand>
{
	private bool hasSpawnedHand;

	private bool holdInputActive;

	public override bool IsInstantCast => false;

	internal bool HoldInputActive => holdInputActive;

	internal void Configure(SpellData data)
	{
		((Spell)this).spellData = data;
	}

	public override void OnSpellActivate()
	{
		hasSpawnedHand = false;
		ControlInputRelay component = ((Component)this).GetComponent<ControlInputRelay>();
		holdInputActive = (Object)(object)component != (Object)null && component.ServerHoldActive;
		Plugin.Log.LogDebug((object)$"Control activated on server; R held: {holdInputActive}.");
		base.OnSpellActivate();
	}

	[Server]
	internal void SetHoldInput(bool active)
	{
		if (NetworkServer.active)
		{
			holdInputActive = active;
		}
	}

	[Server]
	protected override void SpawnSingleObject(Vector3 position, Quaternion rotation)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		if (!NetworkServer.active)
		{
			Debug.LogWarning((object)"[ControlSpell] Spawn attempted while the server was inactive.");
			return;
		}
		if ((Object)(object)((Spell)this).spellData == (Object)null || (Object)(object)((Spell)this).spellData.objectToSpawnPrefab == (Object)null)
		{
			Debug.LogError((object)"[ControlSpell] Runtime SpellData or hand prefab is missing.");
			((Spell)this).StopSpell();
			return;
		}
		GameObject val = Object.Instantiate<GameObject>(((Spell)this).spellData.objectToSpawnPrefab, position, rotation);
		val.SetActive(true);
		ControlProjectileHand component = val.GetComponent<ControlProjectileHand>();
		if ((Object)(object)component == (Object)null)
		{
			Debug.LogError((object)"[ControlSpell] Spawned prefab has no ControlProjectileHand.");
			Object.Destroy((Object)(object)val);
			((Spell)this).StopSpell();
			return;
		}
		NetworkServer.Spawn(val, (NetworkConnectionToClient)null);
		((BaseProjectile)component).Initialize(((Spell)this).NetworkcasterIdentity, (Spell)(object)this);
		base.activeSpawnedObjects.Add(component);
		hasSpawnedHand = true;
		((SpawnableObjectSpell<ControlProjectileHand>)this).OnObjectInitialized(component);
		((Spell)this).RpcPlayEffect((SpellEffectType)1, position, rotation);
		Plugin.Log.LogDebug((object)$"Control hand spawned; R held: {holdInputActive}.");
	}

	[Server]
	internal bool TryApplyAction(ControlActionKind actionKind, ControlDirection direction)
	{
		if (!NetworkServer.active || !holdInputActive || (Object)(object)base.activeSpawnedObject == (Object)null || !base.activeSpawnedObject.HasGrabbedTarget)
		{
			return false;
		}
		ControlProjectileHand activeSpawnedObject = base.activeSpawnedObject;
		switch (actionKind)
		{
		case ControlActionKind.Closer:
			activeSpawnedObject.MoveCloser();
			return false;
		case ControlActionKind.Raise:
			activeSpawnedObject.Raise();
			return false;
		case ControlActionKind.SlamDown:
			activeSpawnedObject.SlamDown(Plugin.ControlThrowForce);
			return false;
		case ControlActionKind.Throw:
			activeSpawnedObject.ThrowAndDestroy(Plugin.ControlThrowForce, direction);
			base.activeSpawnedObjects.Clear();
			((Spell)this).StopSpell();
			return true;
		default:
			return false;
		}
	}

	[Server]
	public override void OnSpellDeactivate()
	{
		if (!NetworkServer.active)
		{
			Debug.LogWarning((object)"[ControlSpell] Deactivation attempted while the server was inactive.");
			return;
		}
		((MonoBehaviour)this).StopAllCoroutines();
		ControlProjectileHand activeSpawnedObject = base.activeSpawnedObject;
		if ((Object)(object)activeSpawnedObject != (Object)null)
		{
			activeSpawnedObject.DropAndDestroy();
		}
		base.activeSpawnedObjects.Clear();
		hasSpawnedHand = false;
		holdInputActive = false;
		Plugin.Log.LogDebug((object)"Control deactivated on server.");
		base.OnSpellDeactivate();
	}

	public override void Update()
	{
		((Spell)this).Update();
		if (((NetworkBehaviour)this).isServer && ((Spell)this).isCasting && hasSpawnedHand && (Object)(object)base.activeSpawnedObject == (Object)null)
		{
			((Spell)this).StopSpell();
			VoiceSpell component = ((Component)this).GetComponent<VoiceSpell>();
			if ((Object)(object)component != (Object)null && (Object)(object)component.NetworkcurrentSpell == (Object)(object)this)
			{
				component.NetworkcurrentSpell = null;
				((Spell)component).NetworkisCasting = false;
				component.StopPrimingAnimation();
			}
		}
	}
}
public sealed class FireBallProjectile : BaseProjectile
{
	[SerializeField]
	private GameObject explosionVfxPrefab;

	[SerializeField]
	private NetworkIdentity explosionPrefab;

	[SerializeField]
	private EventReference explosionSfx;

	private bool hasExploded;

	internal void Configure(ProjectileData data, SpellData ownerSpellData, GameObject igniteBoomExplosionVfx, NetworkIdentity igniteBoomExplosionPrefab, EventReference igniteBoomExplosionSfx, float lifetime)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		base.projectileData = data;
		base.spellData = ownerSpellData;
		explosionVfxPrefab = igniteBoomExplosionVfx;
		explosionPrefab = igniteBoomExplosionPrefab;
		explosionSfx = igniteBoomExplosionSfx;
		base.ttl = lifetime;
		base.ignoreCaster = true;
		base.delayProcessObstacles = true;
	}

	protected override void OnTargetHit(Collider target, Rigidbody targetRigidbody, NetworkIdentity targetIdentity)
	{
		if (((NetworkBehaviour)this).isServer && !hasExploded)
		{
			TriggerIgniteBoomExplosion();
		}
	}

	protected override void OnWallHit(Collider wall)
	{
		if (((NetworkBehaviour)this).isServer && !hasExploded)
		{
			TriggerIgniteBoomExplosion();
		}
		((BaseProjectile)this).OnWallHit(wall);
	}

	[Server]
	private void TriggerIgniteBoomExplosion()
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		if (!NetworkServer.active || hasExploded)
		{
			return;
		}
		hasExploded = true;
		Vector3 position = ((Component)this).transform.position;
		Quaternion rotation = ((Component)this).transform.rotation;
		if ((Object)(object)explosionVfxPrefab != (Object)null && (Object)(object)VfxManager.Instance != (Object)null)
		{
			int vfxId = VfxManager.Instance.GetVfxId(explosionVfxPrefab);
			if (vfxId != -1)
			{
				GameManager.Instance.RpcSpawnVfx(vfxId, position);
			}
		}
		AudioManager val = default(AudioManager);
		if (!((EventReference)(ref explosionSfx)).IsNull && Service.Get<AudioManager>(ref val))
		{
			int eventId = val.GetEventId(explosionSfx);
			if (eventId != -1)
			{
				GameManager.Instance.RpcPlaySfxEvent(eventId, position);
			}
		}
		if ((Object)(object)explosionPrefab != (Object)null)
		{
			GameManager.SpawnNetworkPrefab(explosionPrefab, position, rotation, true);
		}
	}
}
public sealed class FireBallSpell : SpawnableObjectSpell<BaseProjectile>
{
	internal void Configure(SpellData data)
	{
		((Spell)this).spellData = data;
	}

	[Server]
	protected override void SpawnSingleObject(Vector3 position, Quaternion rotation)
	{
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		if (!NetworkServer.active)
		{
			Debug.LogWarning((object)"[FireBallSpell] Spawn attempted while the server was inactive.");
			return;
		}
		if ((Object)(object)((Spell)this).spellData == (Object)null || (Object)(object)((Spell)this).spellData.objectToSpawnPrefab == (Object)null)
		{
			Debug.LogError((object)"[FireBallSpell] Runtime SpellData or projectile prefab is missing.");
			return;
		}
		GameObject val = Object.Instantiate<GameObject>(((Spell)this).spellData.objectToSpawnPrefab, position, rotation);
		val.SetActive(true);
		BaseProjectile component = val.GetComponent<BaseProjectile>();
		if ((Object)(object)component == (Object)null)
		{
			Debug.LogError((object)"[FireBallSpell] Spawned prefab has no BaseProjectile.");
			Object.Destroy((Object)(object)val);
			return;
		}
		NetworkServer.Spawn(val, (NetworkConnectionToClient)null);
		component.Initialize(((Spell)this).NetworkcasterIdentity, (Spell)(object)this);
		base.activeSpawnedObjects.Add(component);
		((SpawnableObjectSpell<BaseProjectile>)this).OnObjectInitialized(component);
		((Spell)this).RpcPlayEffect((SpellEffectType)1, position, rotation);
	}
}
[BepInPlugin("Hero_brian124.Herobrian_spells", "Herobrian_spells", "1.0.0")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "Hero_brian124.Herobrian_spells";

	public const string PluginName = "Herobrian_spells";

	public const string PluginVersion = "1.0.0";

	public const string VoiceCommandKey = "MOD_SPELL_FIR_FIRE_BALL";

	public const string ControlVoiceCommandKey = "MOD_SPELL_ARC_CONTROL";

	public const string VomitVoiceCommandKey = "MOD_SPELL_GRO_VOMIT";

	public const string ZawardoVoiceCommandKey = "MOD_SPELL_BAS_ZAWARDO";

	internal const uint ProjectileAssetId = 4051612689u;

	internal const uint ControlHandAssetId = 3229223441u;

	internal const uint VomitStreamAssetId = 2963347217u;

	private Harmony _harmony;

	internal static ManualLogSource Log { get; private set; }

	internal static string[] PhraseTokens { get; private set; } = new string[2] { "FIRE", "BALL" };

	internal static string[] ControlPhraseTokens { get; private set; } = new string[1] { "CONTROL" };

	internal static string[] VomitPhraseTokens { get; private set; } = new string[1] { "VOMIT" };

	internal static string[] ZawardoPhraseTokens { get; private set; } = new string[2] { "THE", "WORLD" };

	internal static float ProjectileSpeed { get; private set; }

	internal static float ProjectileRange { get; private set; }

	internal static float ProjectileLifetime { get; private set; }

	internal static float Cooldown { get; private set; }

	internal static int Charges { get; private set; }

	internal static float ChargeRegenSeconds { get; private set; }

	internal static float ControlHoldStrength { get; private set; }

	internal static float ControlThrowForce { get; private set; }

	internal static Key ControlHoldKey { get; private set; } = (Key)32;

	private void Awake()
	{
		//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d7: 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_02f5: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		ConfigEntry<string> val = ((BaseUnityPlugin)this).Config.Bind<string>("Voice", "Phrase", "FIRE BALL", "Words spoken to cast the spell. Restart the game after changing this.");
		ConfigEntry<float> val2 = ((BaseUnityPlugin)this).Config.Bind<float>("Projectile", "Speed", 15f, "Projectile speed.");
		ConfigEntry<float> val3 = ((BaseUnityPlugin)this).Config.Bind<float>("Projectile", "Range", 30f, "Maximum travel distance.");
		ConfigEntry<float> val4 = ((BaseUnityPlugin)this).Config.Bind<float>("Projectile", "Lifetime", 5f, "Maximum projectile lifetime.");
		ConfigEntry<float> val5 = ((BaseUnityPlugin)this).Config.Bind<float>("Spell", "Cooldown", 1f, "Cooldown after casting.");
		ConfigEntry<int> val6 = ((BaseUnityPlugin)this).Config.Bind<int>("Spell", "Charges", 3, "Maximum charges.");
		ConfigEntry<float> val7 = ((BaseUnityPlugin)this).Config.Bind<float>("Spell", "ChargeRegenSeconds", 8f, "Seconds to regenerate one charge.");
		ConfigEntry<string> obj = ((BaseUnityPlugin)this).Config.Bind<string>("Control Voice", "ControlPhrase", "CONTROL", "Words spoken to start Control. Restart after changing this.");
		ConfigEntry<string> val8 = ((BaseUnityPlugin)this).Config.Bind<string>("Vomit Voice", "VomitPhrase", "VOMIT", "Words spoken to cast Vomit Spell. Restart after changing this.");
		ConfigEntry<string> val9 = ((BaseUnityPlugin)this).Config.Bind<string>("The World Voice", "TheWorldPhrase", "THE WORLD", "Words spoken to cast The World. Restart after changing this.");
		ConfigEntry<float> val10 = ((BaseUnityPlugin)this).Config.Bind<float>("Control", "HoldStrength", 8f, "Correction strength used to keep players, NPCs, and props held.");
		ConfigEntry<float> val11 = ((BaseUnityPlugin)this).Config.Bind<float>("Control", "ThrowForce", 30f, "Force applied when the caster says THROW.");
		ConfigEntry<Key> val12 = ((BaseUnityPlugin)this).Config.Bind<Key>("Control", "HoldKey", (Key)32, "Keyboard key held to keep the Control hand attached.");
		PhraseTokens = NormalizePhrase(val.Value);
		ControlPhraseTokens = NormalizePhrase(obj.Value, "CONTROL");
		VomitPhraseTokens = NormalizePhrase(val8.Value, "VOMIT");
		ZawardoPhraseTokens = NormalizePhrase(val9.Value, "THE", "WORLD");
		ProjectileSpeed = Math.Max(1f, val2.Value);
		ProjectileRange = Math.Max(1f, val3.Value);
		ProjectileLifetime = Math.Max(0.5f, val4.Value);
		Cooldown = Math.Max(0f, val5.Value);
		Charges = Math.Max(1, val6.Value);
		ChargeRegenSeconds = Math.Max(0.1f, val7.Value);
		ControlHoldStrength = Math.Max(1f, val10.Value);
		ControlThrowForce = Math.Max(1f, val11.Value);
		ControlHoldKey = (Key)(((int)val12.Value == 0) ? 32 : ((int)val12.Value));
		_harmony = new Harmony("Hero_brian124.Herobrian_spells");
		_harmony.PatchAll(typeof(Plugin).Assembly);
		((Component)this).gameObject.AddComponent<ZawardoClientController>();
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Loaded Herobrian_spells 1.0.0; Fire Ball: " + string.Join(" ", PhraseTokens) + "; Control: " + string.Join(" ", ControlPhraseTokens) + "; Vomit Spell: " + string.Join(" ", VomitPhraseTokens) + "; The World: " + string.Join(" ", ZawardoPhraseTokens) + "; " + $"{ControlActionCatalog.VoiceActions.Length} Control voice actions"));
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}

	private static string[] NormalizePhrase(string phrase, params string[] fallback)
	{
		string[] array = (from token in (phrase ?? string.Empty).Split(new char[3] { ' ', '-', '\t' }, StringSplitOptions.RemoveEmptyEntries)
			select token.Trim().ToUpperInvariant() into token
			where token.Length > 0
			select token).ToArray();
		if (array.Length == 0)
		{
			if (fallback.Length != 0)
			{
				return fallback;
			}
			return new string[2] { "FIRE", "BALL" };
		}
		return array;
	}

	internal static IEnumerable<KeyValuePair<string, string[]>> GetVoiceCommands()
	{
		yield return new KeyValuePair<string, string[]>("MOD_SPELL_FIR_FIRE_BALL", PhraseTokens);
		yield return new KeyValuePair<string, string[]>("MOD_SPELL_ARC_CONTROL", ControlPhraseTokens);
		yield return new KeyValuePair<string, string[]>("MOD_SPELL_GRO_VOMIT", VomitPhraseTokens);
		yield return new KeyValuePair<string, string[]>("MOD_SPELL_BAS_ZAWARDO", ZawardoPhraseTokens);
		foreach (KeyValuePair<string, string[]> voiceCommand in ControlActionCatalog.GetVoiceCommands())
		{
			yield return voiceCommand;
		}
	}

	internal static bool TryGetVoiceCommand(string key, out string[] tokens)
	{
		foreach (KeyValuePair<string, string[]> voiceCommand in GetVoiceCommands())
		{
			if (string.Equals(key, voiceCommand.Key, StringComparison.Ordinal))
			{
				tokens = voiceCommand.Value;
				return true;
			}
		}
		tokens = null;
		return false;
	}
}
internal static class FireBallBootstrap
{
	private static readonly FieldInfo VoiceSpellsField = AccessTools.Field(typeof(VoiceSpell), "spells");

	private static readonly FieldInfo MinePrefabField = AccessTools.Field(typeof(MineSpell), "minePrefab");

	private static readonly FieldInfo MineSpawnSfxField = AccessTools.Field(typeof(MineTrigger), "spawnSfx");

	private static readonly FieldInfo MineExplosionSfxField = AccessTools.Field(typeof(MineTrigger), "explosionSfx");

	private static readonly FieldInfo NetworkAssetIdField = AccessTools.Field(typeof(NetworkIdentity), "_assetId");

	private static readonly FieldInfo NetworkHasSpawnedField = AccessTools.Field(typeof(NetworkIdentity), "hasSpawned");

	private static GameObject _networkProjectilePrefab;

	private static SpellData _spellData;

	private static ProjectileData _projectileData;

	private static EventReference _igniteBoomSpawnSfx;

	private static EventReference _igniteBoomExplosionSfx;

	internal static void Install(YapNetworkManager manager)
	{
		if ((Object)(object)manager == (Object)null)
		{
			return;
		}
		GameObject val = FindPrefab(manager, "PropWandOfFire");
		GameObject val2 = FindPrefab(manager, "Fireball");
		if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
		{
			Plugin.Log.LogError((object)"Fire Ball was not installed: PropWandOfFire or Fireball was not found in YapNetworkManager.spawnPrefabs.");
			return;
		}
		NetworkIdentity val3 = default(NetworkIdentity);
		if (((NetworkManager)manager).spawnPrefabs.Any((GameObject prefab) => (Object)(object)prefab != (Object)null && prefab.TryGetComponent<NetworkIdentity>(ref val3) && val3.assetId == 4051612689u))
		{
			Plugin.Log.LogError((object)$"Fire Ball asset ID collision: {4051612689u}. The mod will not install.");
			return;
		}
		VoiceSpell component = val.GetComponent<VoiceSpell>();
		LaunchSelfSpell component2 = val.GetComponent<LaunchSelfSpell>();
		MineSpell component3 = val.GetComponent<MineSpell>();
		if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || (Object)(object)component3 == (Object)null)
		{
			Plugin.Log.LogError((object)"Fire Ball was not installed: the Fire Wand is missing VoiceSpell, LaunchSelfSpell, or MineSpell.");
			return;
		}
		EnsureRuntimeData(manager, component2, component3);
		EnsureProjectilePrefab(val2, component3);
		EnsureSpellOnWand(val, component);
	}

	private static GameObject FindPrefab(YapNetworkManager manager, string name)
	{
		return ((IEnumerable<GameObject>)((NetworkManager)manager).spawnPrefabs).FirstOrDefault((Func<GameObject, bool>)((GameObject prefab) => (Object)(object)prefab != (Object)null && string.Equals(((Object)prefab).name, name, StringComparison.Ordinal)));
	}

	private static void EnsureRuntimeData(YapNetworkManager manager, LaunchSelfSpell launchSelfSpell, MineSpell mineSpell)
	{
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: 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_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		ReadIgniteBoomAudio(mineSpell);
		if ((Object)(object)_spellData == (Object)null)
		{
			_spellData = Object.Instantiate<SpellData>(((Spell)launchSelfSpell).SpellData);
			((Object)_spellData).name = "SD_FIR_FIRE_BALL_MOD";
			_spellData.spellName = "FIRE BALL";
			_spellData.voiceCommandKey = "MOD_SPELL_FIR_FIRE_BALL";
			_spellData.cooldown = Plugin.Cooldown;
			_spellData.charges = Plugin.Charges;
			_spellData.chargePerCast = 1;
			_spellData.chargeCostPerSecondCast = 0;
			_spellData.chargeRegenInterval = Plugin.ChargeRegenSeconds;
			_spellData.chargesRegenPerInterval = 1;
			_spellData.chargeRegenDelay = 0.5f;
			_spellData.spawnLocation = (SpawnLocation)1;
			_spellData.spawnOffset = Vector3.zero;
			_spellData.applyLookTransform = true;
			_spellData.spawnDelay = 0.1f;
			_spellData.castEvent = _igniteBoomSpawnSfx;
			_spellData.impactEvent = default(EventReference);
			_spellData.impactVFXPrefab = null;
		}
		if (!((Object)(object)_projectileData == (Object)null))
		{
			return;
		}
		BaseProjectile val = (from prefab in ((NetworkManager)manager).spawnPrefabs
			where (Object)(object)prefab != (Object)null
			select prefab.GetComponent<BaseProjectile>()).FirstOrDefault((Func<BaseProjectile, bool>)((BaseProjectile projectile) => (Object)(object)projectile != (Object)null && (Object)(object)projectile.ProjectileData != (Object)null && projectile is ProjectileTornado));
		if ((Object)(object)val == (Object)null)
		{
			val = (from prefab in ((NetworkManager)manager).spawnPrefabs
				where (Object)(object)prefab != (Object)null
				select prefab.GetComponent<BaseProjectile>()).FirstOrDefault((Func<BaseProjectile, bool>)((BaseProjectile projectile) => (Object)(object)projectile != (Object)null && (Object)(object)projectile.ProjectileData != (Object)null && ((LayerMask)(ref projectile.ProjectileData.wallLayer)).value != 0));
		}
		if ((Object)(object)val == (Object)null)
		{
			throw new InvalidOperationException("No vanilla projectile with a wall collision profile was found.");
		}
		_projectileData = Object.Instantiate<ProjectileData>(val.ProjectileData);
		((Object)_projectileData).name = "PD_FIR_FIRE_BALL_MOD";
		_projectileData.speed = Plugin.ProjectileSpeed;
		_projectileData.gravity = 0f;
		_projectileData.maxTravelDistance = Plugin.ProjectileRange;
		_projectileData.spawnInvulnerability = 0.15f;
		_projectileData.enablePiercing = false;
		_projectileData.maxPierceCount = 0;
		_projectileData.projectileCount = 1;
		_projectileData.maxProjectileCount = 0;
		_projectileData.spreadAngle = 0f;
		_projectileData.useRandomSpread = false;
		_projectileData.verticalDegreeRandomness = 0f;
		_projectileData.projectileFireDelay = 0f;
		_projectileData.chanceForMultiShot = 0f;
	}

	private static void ReadIgniteBoomAudio(MineSpell mineSpell)
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//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)
		object? value = MinePrefabField.GetValue(mineSpell);
		NetworkIdentity val = (NetworkIdentity)((value is NetworkIdentity) ? value : null);
		MineTrigger val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent<MineTrigger>());
		if ((Object)(object)val2 == (Object)null)
		{
			throw new InvalidOperationException("Ignite Boom's mine prefab or MineTrigger could not be resolved.");
		}
		_igniteBoomSpawnSfx = (EventReference)MineSpawnSfxField.GetValue(val2);
		_igniteBoomExplosionSfx = (EventReference)MineExplosionSfxField.GetValue(val2);
		if (((EventReference)(ref _igniteBoomSpawnSfx)).IsNull || ((EventReference)(ref _igniteBoomExplosionSfx)).IsNull)
		{
			throw new InvalidOperationException("Ignite Boom's spawn or explosion FMOD event is empty.");
		}
	}

	private static void EnsureProjectilePrefab(GameObject vanillaFireballPrefab, MineSpell mineSpell)
	{
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_networkProjectilePrefab != (Object)null)
		{
			_spellData.objectToSpawnPrefab = _networkProjectilePrefab;
			return;
		}
		bool activeSelf = vanillaFireballPrefab.activeSelf;
		try
		{
			vanillaFireballPrefab.SetActive(false);
			_networkProjectilePrefab = Object.Instantiate<GameObject>(vanillaFireballPrefab);
		}
		finally
		{
			vanillaFireballPrefab.SetActive(activeSelf);
		}
		((Object)_networkProjectilePrefab).name = "FireBallModProjectile";
		_networkProjectilePrefab.SetActive(false);
		Object.DontDestroyOnLoad((Object)(object)_networkProjectilePrefab);
		NetworkIdentity component = _networkProjectilePrefab.GetComponent<NetworkIdentity>();
		if ((Object)(object)component == (Object)null)
		{
			throw new InvalidOperationException("The cloned Fireball prefab has no NetworkIdentity.");
		}
		NetworkAssetIdField.SetValue(component, 4051612689u);
		NetworkHasSpawnedField.SetValue(component, false);
		component.sceneId = 0uL;
		NetworkBehaviour[] components = _networkProjectilePrefab.GetComponents<NetworkBehaviour>();
		foreach (NetworkBehaviour val in components)
		{
			if (((object)val).GetType().Name.IndexOf("NetworkTransform", StringComparison.Ordinal) >= 0)
			{
				val.syncDirection = (SyncDirection)0;
			}
		}
		Rigidbody[] componentsInChildren = _networkProjectilePrefab.GetComponentsInChildren<Rigidbody>(true);
		foreach (Rigidbody obj in componentsInChildren)
		{
			obj.useGravity = false;
			obj.isKinematic = true;
			obj.detectCollisions = true;
		}
		Collider[] componentsInChildren2 = _networkProjectilePrefab.GetComponentsInChildren<Collider>(true);
		for (int i = 0; i < componentsInChildren2.Length; i++)
		{
			componentsInChildren2[i].isTrigger = true;
		}
		DisableIfPresent<Interactable>(_networkProjectilePrefab);
		DisableIfPresent<NetworkPhysicsPropDestroy>(_networkProjectilePrefab);
		DisableIfPresent<RigidbodyConfig>(_networkProjectilePrefab);
		(_networkProjectilePrefab.GetComponent<FireBallProjectile>() ?? _networkProjectilePrefab.AddComponent<FireBallProjectile>()).Configure(_projectileData, _spellData, mineSpell.ExplosionVfxPrefab, mineSpell.ExplosionPrefab, _igniteBoomExplosionSfx, Plugin.ProjectileLifetime);
		_spellData.objectToSpawnPrefab = _networkProjectilePrefab;
		Plugin.Log.LogInfo((object)$"Prepared Fire Ball projectile prefab with asset ID {4051612689u}.");
	}

	private static void DisableIfPresent<T>(GameObject gameObject) where T : Behaviour
	{
		T component = gameObject.GetComponent<T>();
		if ((Object)(object)component != (Object)null)
		{
			((Behaviour)component).enabled = false;
		}
	}

	private static void EnsureSpellOnWand(GameObject fireWandPrefab, VoiceSpell voiceSpell)
	{
		FireBallSpell fireBallSpell = fireWandPrefab.GetComponent<FireBallSpell>() ?? fireWandPrefab.AddComponent<FireBallSpell>();
		fireBallSpell.Configure(_spellData);
		((Behaviour)fireBallSpell).enabled = true;
		Spell[] array = (VoiceSpellsField.GetValue(voiceSpell) as Spell[]) ?? Array.Empty<Spell>();
		if (!array.Contains((Spell)(object)fireBallSpell))
		{
			VoiceSpellsField.SetValue(voiceSpell, array.Concat((IEnumerable<Spell>)(object)new Spell[1] { (Spell)fireBallSpell }).ToArray());
			Plugin.Log.LogInfo((object)$"Added Fire Ball to PropWandOfFire ({array.Length + 1} total spell entries).");
		}
	}

	internal static void RegisterClientSpawnHandler()
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Expected O, but got Unknown
		//IL_0049: Expected O, but got Unknown
		if ((Object)(object)_networkProjectilePrefab == (Object)null)
		{
			Plugin.Log.LogWarning((object)"Cannot register Fire Ball client spawn handler: prefab is not prepared.");
			return;
		}
		NetworkClient.UnregisterSpawnHandler(4051612689u);
		NetworkClient.RegisterSpawnHandler(4051612689u, new SpawnHandlerDelegate(SpawnClientProjectile), new UnSpawnDelegate(UnspawnClientProjectile));
		Plugin.Log.LogInfo((object)$"Registered Fire Ball client spawn handler for asset ID {4051612689u}.");
	}

	private static GameObject SpawnClientProjectile(SpawnMessage message)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		GameObject obj = Object.Instantiate<GameObject>(_networkProjectilePrefab, message.position, message.rotation);
		obj.SetActive(true);
		return obj;
	}

	private static void UnspawnClientProjectile(GameObject instance)
	{
		if ((Object)(object)instance != (Object)null)
		{
			Object.Destroy((Object)(object)instance);
		}
	}
}
[HarmonyPatch(typeof(YapNetworkManager), "Initialise")]
internal static class NetworkManagerInitialisePatch
{
	[HarmonyPrefix]
	private static void Prefix(YapNetworkManager __instance)
	{
		try
		{
			FireBallBootstrap.Install(__instance);
			ControlBootstrap.Install(__instance);
			VomitBootstrap.Install(__instance);
			ZawardoBootstrap.Install(__instance);
		}
		catch (Exception arg)
		{
			Plugin.Log.LogError((object)$"Spell expansion installation failed: {arg}");
		}
	}
}
[HarmonyPatch(typeof(YapNetworkManager), "OnStartClient")]
internal static class NetworkManagerStartClientPatch
{
	[HarmonyPostfix]
	private static void Postfix()
	{
		try
		{
			FireBallBootstrap.RegisterClientSpawnHandler();
			ControlBootstrap.RegisterClientSpawnHandler();
			VomitBootstrap.RegisterClientSpawnHandler();
		}
		catch (Exception arg)
		{
			Plugin.Log.LogError((object)$"Spell expansion client registration failed: {arg}");
		}
	}
}
[HarmonyPatch(typeof(VoiceManager), "LoadLocalisationData")]
internal static class VoiceLocalisationLoadPatch
{
	[HarmonyPostfix]
	private static void Postfix(VoiceManager __instance)
	{
		VoicePhraseRegistration.AddToAllTranslators(__instance);
	}
}
[HarmonyPatch(typeof(VoiceManager), "SetLanguage")]
internal static class VoiceLanguagePatch
{
	[HarmonyPrefix]
	private static void Prefix(VoiceManager __instance)
	{
		VoicePhraseRegistration.AddToCurrentTranslator(__instance);
	}
}
[HarmonyPatch(typeof(VoiceManager), "TryGetVoiceCommand")]
internal static class VoiceCommandLookupPatch
{
	[HarmonyPrefix]
	private static bool Prefix(string key, ref string[] command, ref bool __result)
	{
		if (!Plugin.TryGetVoiceCommand(key, out var tokens))
		{
			return true;
		}
		command = tokens;
		__result = true;
		return false;
	}
}
internal static class VoicePhraseRegistration
{
	private static readonly FieldInfo TranslatorsField = AccessTools.Field(typeof(VoiceManager), "_voskTranslators");

	private static readonly FieldInfo CurrentTranslatorField = AccessTools.Field(typeof(VoiceManager), "_currentVoskTranslator");

	internal static void AddToAllTranslators(VoiceManager manager)
	{
		try
		{
			if (!(TranslatorsField.GetValue(manager) is IEnumerable enumerable))
			{
				return;
			}
			foreach (object item in enumerable)
			{
				AddToTranslator(item);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Could not extend all Vosk translators: " + ex.Message));
		}
	}

	internal static void AddToCurrentTranslator(VoiceManager manager)
	{
		try
		{
			AddToTranslator(CurrentTranslatorField.GetValue(manager));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Could not extend the current Vosk translator: " + ex.Message));
		}
	}

	private static void AddToTranslator(object translator)
	{
		if (translator == null)
		{
			return;
		}
		Type type = translator.GetType();
		FieldInfo fieldInfo = AccessTools.Field(type, "_commands");
		FieldInfo fieldInfo2 = AccessTools.Field(type, "_grammar");
		if (fieldInfo.GetValue(translator) is Dictionary<string, string[]> dictionary)
		{
			foreach (KeyValuePair<string, string[]> voiceCommand in Plugin.GetVoiceCommands())
			{
				dictionary[voiceCommand.Key] = voiceCommand.Value;
			}
		}
		string[] value = ((fieldInfo2.GetValue(translator) as string[]) ?? Array.Empty<string>()).Concat(Plugin.GetVoiceCommands().SelectMany((KeyValuePair<string, string[]> command) => command.Value)).Distinct<string>(StringComparer.Ordinal).ToArray();
		fieldInfo2.SetValue(translator, value);
	}
}
internal static class VomitBootstrap
{
	private const float VisualScale = 1.2f;

	private static readonly FieldInfo VoiceSpellsField = AccessTools.Field(typeof(VoiceSpell), "spells");

	private static readonly FieldInfo ControllerSpellDataField = AccessTools.Field(typeof(BaseChanneledController), "spellData");

	private static readonly FieldInfo ControllerDataField = AccessTools.Field(typeof(BaseChanneledController), "controllerData");

	private static readonly FieldInfo NetworkAssetIdField = AccessTools.Field(typeof(NetworkIdentity), "_assetId");

	private static readonly FieldInfo NetworkHasSpawnedField = AccessTools.Field(typeof(NetworkIdentity), "hasSpawned");

	private static readonly FieldInfo StreamSizeMultiplierField = AccessTools.Field(typeof(ChanneledStreamView), "sizeMultiplier");

	private static readonly FieldInfo StreamViewField = AccessTools.Field(typeof(ChanneledStream), "streamVFX");

	private static GameObject networkVomitPrefab;

	private static SpellData vomitSpellData;

	internal static void Install(YapNetworkManager manager)
	{
		if ((Object)(object)manager == (Object)null)
		{
			return;
		}
		GameObject val = FindPrefab(manager, "PropWandOfGrotesque");
		GameObject val2 = FindPrefab(manager, "PropWandOfFire");
		GameObject val3 = FindPrefab(manager, "ChanneledStream");
		GameObject val4 = FindPrefab(manager, "ChanneledStream_Lava");
		if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null)
		{
			Plugin.Log.LogError((object)"Vomit Spell was not installed: Grotesque, Fire Wand, Grotesque stream, or Ignited Tongue stream was not found.");
			return;
		}
		NetworkIdentity val6 = default(NetworkIdentity);
		if (((NetworkManager)manager).spawnPrefabs.Any((GameObject prefab) => (Object)(object)prefab != (Object)null && prefab.TryGetComponent<NetworkIdentity>(ref val6) && val6.assetId == 2963347217u))
		{
			Plugin.Log.LogError((object)$"Vomit stream asset ID collision: {2963347217u}. Vomit Spell will not install.");
			return;
		}
		VoiceSpell component = val.GetComponent<VoiceSpell>();
		PissSpell val5 = ((IEnumerable<PissSpell>)val.GetComponents<PissSpell>()).FirstOrDefault((Func<PissSpell, bool>)((PissSpell spell) => (Object)(object)((Spell)spell).SpellData != (Object)null && string.Equals(((Spell)spell).SpellData.spellName, "PISS-YUK", StringComparison.Ordinal)));
		LavaSpell component2 = val2.GetComponent<LavaSpell>();
		LavaStream component3 = val4.GetComponent<LavaStream>();
		if ((Object)(object)component == (Object)null || (Object)(object)val5 == (Object)null || (Object)(object)component2 == (Object)null || (Object)(object)component3 == (Object)null)
		{
			Plugin.Log.LogError((object)"Vomit Spell was not installed: VoiceSpell, Piss Yuk, Ignited Tongue, or its LavaStream is missing.");
			return;
		}
		EnsureRuntimeData(component2, val5);
		EnsureVomitPrefab(val3, component3);
		EnsureSpellOnWand(val, component);
	}

	private static GameObject FindPrefab(YapNetworkManager manager, string name)
	{
		return ((IEnumerable<GameObject>)((NetworkManager)manager).spawnPrefabs).FirstOrDefault((Func<GameObject, bool>)((GameObject prefab) => (Object)(object)prefab != (Object)null && string.Equals(((Object)prefab).name, name, StringComparison.Ordinal)));
	}

	private static void EnsureRuntimeData(LavaSpell ignitedTongue, PissSpell pissYuk)
	{
		//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_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)vomitSpellData != (Object)null))
		{
			vomitSpellData = Object.Instantiate<SpellData>(((Spell)ignitedTongue).SpellData);
			((Object)vomitSpellData).name = "SD_GRO_VOMIT_MOD";
			vomitSpellData.spellName = "VOMIT SPELL";
			vomitSpellData.voiceCommandKey = "MOD_SPELL_GRO_VOMIT";
			vomitSpellData.idleColor = ((Spell)pissYuk).SpellData.idleColor;
			vomitSpellData.cooldownColor = ((Spell)pissYuk).SpellData.cooldownColor;
			vomitSpellData.chargeColor = ((Spell)pissYuk).SpellData.chargeColor;
			vomitSpellData.startEvent = ((Spell)pissYuk).SpellData.startEvent;
			vomitSpellData.loopEvent = ((Spell)pissYuk).SpellData.loopEvent;
			vomitSpellData.endEvent = ((Spell)pissYuk).SpellData.endEvent;
			vomitSpellData.impactEvent = ((Spell)pissYuk).SpellData.impactEvent;
			Plugin.Log.LogInfo((object)("Prepared Vomit Spell data from Ignited Tongue: " + $"{vomitSpellData.charges} charges, {vomitSpellData.cooldown:0.##}s cooldown, " + "Piss Yuk stream audio and combo interactions."));
		}
	}

	private static void EnsureVomitPrefab(GameObject grotesqueStreamPrefab, LavaStream vanillaLavaStream)
	{
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)networkVomitPrefab != (Object)null)
		{
			vomitSpellData.objectToSpawnPrefab = networkVomitPrefab;
			return;
		}
		bool activeSelf = grotesqueStreamPrefab.activeSelf;
		try
		{
			grotesqueStreamPrefab.SetActive(false);
			networkVomitPrefab = Object.Instantiate<GameObject>(grotesqueStreamPrefab);
		}
		finally
		{
			grotesqueStreamPrefab.SetActive(activeSelf);
		}
		((Object)networkVomitPrefab).name = "ChanneledStream_VomitMod";
		networkVomitPrefab.SetActive(false);
		Object.DontDestroyOnLoad((Object)(object)networkVomitPrefab);
		NetworkIdentity component = networkVomitPrefab.GetComponent<NetworkIdentity>();
		if ((Object)(object)component == (Object)null)
		{
			throw new InvalidOperationException("The cloned lava stream has no NetworkIdentity.");
		}
		NetworkAssetIdField.SetValue(component, 2963347217u);
		NetworkHasSpawnedField.SetValue(component, false);
		component.sceneId = 0uL;
		NetworkBehaviour[] components = networkVomitPrefab.GetComponents<NetworkBehaviour>();
		foreach (NetworkBehaviour val in components)
		{
			if (((object)val).GetType().Name.IndexOf("NetworkTransform", StringComparison.Ordinal) >= 0)
			{
				val.syncDirection = (SyncDirection)0;
			}
		}
		ChanneledStream component2 = networkVomitPrefab.GetComponent<ChanneledStream>();
		ChanneledStreamView component3 = networkVomitPrefab.GetComponent<ChanneledStreamView>();
		if ((Object)(object)component2 == (Object)null || (Object)(object)component3 == (Object)null)
		{
			throw new InvalidOperationException("The cloned Grotesque stream is missing ChanneledStream or ChanneledStreamView.");
		}
		ControllerDataField.SetValue(component2, ControllerDataField.GetValue(vanillaLavaStream));
		ControllerSpellDataField.SetValue(component2, vomitSpellData);
		StreamSizeMultiplierField.SetValue(component3, 1.2f);
		ApplyVomitVisuals(networkVomitPrefab);
		networkVomitPrefab.AddComponent<VomitDamageTracker>();
		vomitSpellData.objectToSpawnPrefab = networkVomitPrefab;
		Plugin.Log.LogInfo((object)("Prepared non-fire Grotesque-colored Vomit stream with asset ID " + $"{2963347217u} and {1.2f:0.0}x visual size."));
	}

	private static void ApplyVomitVisuals(GameObject prefab)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			MainModule main = componentsInChildren[i].main;
			((MainModule)(ref m