Decompiled source of Custom Effects v1.1.1

plugins/CustomEffects.dll

Decompiled an hour ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SideLoader;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("CustomEffects")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Custom Effects")]
[assembly: AssemblyTitle("CustomEffects")]
[assembly: AssemblyVersion("1.1.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CustomEffects
{
	[BepInPlugin("com.wrango.customeffects", "Custom Effects", "1.1.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class CustomEffectsPlugin : BaseUnityPlugin
	{
		public const string GUID = "com.wrango.customeffects";

		public const string NAME = "Custom Effects";

		public const string VERSION = "1.1.1";

		public static CustomEffectsPlugin Instance;

		public static Harmony harmony;

		internal static ManualLogSource Log;

		internal void Awake()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			harmony = new Harmony("com.wrango.customeffects");
			harmony.PatchAll();
			Log.LogMessage((object)"Custom Effects 1.1.1 loaded. Effects: SL_UseSkill, SL_Banish, SL_LearnSkillHidden, SL_PlayVFXOnWeapon, SL_PlayVFXOnChar, SL_PlayVFXPulsed, SL_PlayTimedSound, SL_ReliableVisuals.");
		}
	}
	public class SL_Banish : SL_Effect, ICustomModel
	{
		public float Duration = 10f;

		public Type SLTemplateModel => typeof(SL_Banish);

		public Type GameModel => typeof(BanishEffect);

		public override void ApplyToComponent<T>(T component)
		{
			(component as BanishEffect).Duration = Duration;
		}

		public override void SerializeEffect<T>(T effect)
		{
			Duration = (effect as BanishEffect).Duration;
		}
	}
	public class BanishEffect : Effect
	{
		public float Duration = 10f;

		internal static readonly HashSet<string> Banished = new HashSet<string>();

		private static readonly Action<LocalCharacterControl> ControlReleaseTarget = AccessTools.MethodDelegate<Action<LocalCharacterControl>>(AccessTools.Method(typeof(LocalCharacterControl), "ReleaseTarget", (Type[])null, (Type[])null), (object)null, true);

		internal static bool IsBanished(Character c)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)c))
			{
				HashSet<string> banished = Banished;
				UID uID = c.UID;
				return banished.Contains(((UID)(ref uID)).Value);
			}
			return false;
		}

		internal static bool IsLocallyControlled(Character c)
		{
			if (!Object.op_Implicit((Object)(object)c))
			{
				return false;
			}
			if (c.IsAI)
			{
				return PhotonNetwork.isMasterClient;
			}
			return c.IsLocalPlayer;
		}

		private static int ReleaseLocksOn(Character banished)
		{
			int num = 0;
			if (!Object.op_Implicit((Object)(object)CharacterManager.Instance))
			{
				return num;
			}
			foreach (Character value in CharacterManager.Instance.Characters.Values)
			{
				if (!Object.op_Implicit((Object)(object)value) || !value.IsLocalPlayer)
				{
					continue;
				}
				TargetingSystem targetingSystem = value.TargetingSystem;
				if (!((Object)(object)targetingSystem == (Object)null) && targetingSystem.Locked && !((Object)(object)targetingSystem.LockedCharacter != (Object)(object)banished))
				{
					LocalCharacterControl component = ((Component)value).GetComponent<LocalCharacterControl>();
					if (Object.op_Implicit((Object)(object)component) && ControlReleaseTarget != null)
					{
						ControlReleaseTarget(component);
					}
					else
					{
						targetingSystem.ReleaseTarget();
					}
					num++;
				}
			}
			return num;
		}

		internal static void BeginBanish(Character character, float duration, float delay, StatusEffect status)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)character))
			{
				UID uID = character.UID;
				string value = ((UID)(ref uID)).Value;
				if (!Banished.Contains(value) && Object.op_Implicit((Object)(object)CustomEffectsPlugin.Instance))
				{
					Banished.Add(value);
					((MonoBehaviour)CustomEffectsPlugin.Instance).StartCoroutine(BanishRoutineFor(character, value, duration, delay, status));
				}
			}
		}

		protected override void ActivateLocally(Character _affectedCharacter, object[] _infos)
		{
			if (Object.op_Implicit((Object)(object)_affectedCharacter))
			{
				BeginBanish(_affectedCharacter, Duration, 0f, ((Component)this).GetComponentInParent<StatusEffect>());
			}
		}

		private static IEnumerator BanishRoutineFor(Character character, string uid, float duration, float delay, StatusEffect status)
		{
			try
			{
				if (delay > 0f)
				{
					yield return (object)new WaitForSeconds(delay);
				}
				if (!Object.op_Implicit((Object)(object)character))
				{
					yield break;
				}
				bool controls = IsLocallyControlled(character);
				List<Renderer> hiddenRenderers = new List<Renderer>();
				List<Behaviour> disabledHitboxes = new List<Behaviour>();
				List<Collider> disabledColliders = new List<Collider>();
				List<SoundPlayer> silencedSounds = new List<SoundPlayer>();
				List<bool> silencedWasStopOnDisable = new List<bool>();
				List<bool> silencedWasEnabled = new List<bool>();
				bool moveBlockDisabled = false;
				bool controllerDisabled = false;
				Vector3 frozenPosition = ((Component)character).transform.position;
				bool wasQuestNonTargetable = character.QuestNonTargetable;
				CharacterAI ai = ((Component)character).GetComponent<CharacterAI>();
				bool aiWasEnabled = Object.op_Implicit((Object)(object)ai) && ((Behaviour)ai).enabled;
				bool aiRootWasActive = false;
				if (Object.op_Implicit((Object)(object)character.VisualHolderTrans))
				{
					Renderer[] componentsInChildren = ((Component)character.VisualHolderTrans).GetComponentsInChildren<Renderer>(true);
					foreach (Renderer val in componentsInChildren)
					{
						if (Object.op_Implicit((Object)(object)val) && val.enabled)
						{
							val.enabled = false;
							hiddenRenderers.Add(val);
						}
					}
				}
				if (character.Hitboxes != null)
				{
					Hitbox[] hitboxes = character.Hitboxes;
					foreach (Hitbox val2 in hitboxes)
					{
						if (Object.op_Implicit((Object)(object)val2))
						{
							if (((Behaviour)val2).enabled)
							{
								((Behaviour)val2).enabled = false;
								disabledHitboxes.Add((Behaviour)(object)val2);
							}
							Collider component = ((Component)val2).GetComponent<Collider>();
							if (Object.op_Implicit((Object)(object)component) && component.enabled)
							{
								component.enabled = false;
								disabledColliders.Add(component);
							}
						}
					}
				}
				SoundPlayer[] componentsInChildren2 = ((Component)character).GetComponentsInChildren<SoundPlayer>(true);
				foreach (SoundPlayer val3 in componentsInChildren2)
				{
					if (Object.op_Implicit((Object)(object)val3))
					{
						silencedSounds.Add(val3);
						silencedWasStopOnDisable.Add(val3.StopOnDisable);
						silencedWasEnabled.Add(((Behaviour)val3).enabled);
						val3.StopOnDisable = true;
						((Behaviour)val3).enabled = false;
					}
				}
				if (Object.op_Implicit((Object)(object)character.CharMoveBlockCollider) && ((Collider)character.CharMoveBlockCollider).enabled)
				{
					((Collider)character.CharMoveBlockCollider).enabled = false;
					moveBlockDisabled = true;
				}
				if (Object.op_Implicit((Object)(object)character.CharacterController) && ((Collider)character.CharacterController).enabled)
				{
					((Collider)character.CharacterController).enabled = false;
					controllerDisabled = true;
				}
				character.QuestNonTargetable = true;
				int num = ReleaseLocksOn(character);
				if (controls)
				{
					if (controllerDisabled)
					{
						frozenPosition = ((Component)character).transform.position;
					}
					if (Object.op_Implicit((Object)(object)ai))
					{
						aiRootWasActive = Object.op_Implicit((Object)(object)ai.AIStatesRoot) && ((Component)ai.AIStatesRoot).gameObject.activeSelf;
						if (aiRootWasActive)
						{
							ai.SetNPCActive(false);
						}
						((Behaviour)ai).enabled = false;
					}
					character.Stun(-1f);
					if (Object.op_Implicit((Object)(object)character.Animator))
					{
						character.Animator.speed = 0f;
					}
				}
				CustomEffectsPlugin.Log.LogMessage((object)("[Banish] t=" + Time.time.ToString("F2") + " " + character.Name + " for " + duration + "s | controls=" + controls + " renderers=" + hiddenRenderers.Count + " sounds=" + silencedSounds.Count + " hitboxes=" + disabledHitboxes.Count + " locksReleased=" + num + " aiRootStopped=" + aiRootWasActive));
				float elapsed = 0f;
				bool died = false;
				bool followStatus = !controls && Object.op_Implicit((Object)(object)status);
				while (Object.op_Implicit((Object)(object)character) && elapsed < duration)
				{
					if (!character.Alive)
					{
						died = true;
						break;
					}
					if (followStatus && !Object.op_Implicit((Object)(object)status))
					{
						break;
					}
					if (controls && controllerDisabled)
					{
						((Component)character).transform.position = frozenPosition;
					}
					elapsed += Time.deltaTime;
					yield return null;
				}
				if (!Object.op_Implicit((Object)(object)character))
				{
					yield break;
				}
				for (int j = 0; j < silencedSounds.Count; j++)
				{
					if (Object.op_Implicit((Object)(object)silencedSounds[j]))
					{
						((Behaviour)silencedSounds[j]).enabled = silencedWasEnabled[j];
						silencedSounds[j].StopOnDisable = silencedWasStopOnDisable[j];
					}
				}
				foreach (Renderer item in hiddenRenderers)
				{
					if (Object.op_Implicit((Object)(object)item))
					{
						item.enabled = true;
					}
				}
				foreach (Behaviour item2 in disabledHitboxes)
				{
					if (Object.op_Implicit((Object)(object)item2))
					{
						item2.enabled = true;
					}
				}
				foreach (Collider item3 in disabledColliders)
				{
					if (Object.op_Implicit((Object)(object)item3))
					{
						item3.enabled = true;
					}
				}
				bool flag = died || !character.Alive;
				if (controllerDisabled && Object.op_Implicit((Object)(object)character.CharacterController) && !flag)
				{
					if (controls)
					{
						((Component)character).transform.position = frozenPosition;
					}
					((Collider)character.CharacterController).enabled = true;
				}
				character.QuestNonTargetable = wasQuestNonTargetable;
				ReleaseLocksOn(character);
				if (controls)
				{
					character.StopStun();
					if (Object.op_Implicit((Object)(object)ai))
					{
						if (aiWasEnabled)
						{
							((Behaviour)ai).enabled = true;
						}
						if (aiRootWasActive && !died && character.Alive)
						{
							ai.SetNPCActive(true);
						}
					}
				}
				if (moveBlockDisabled && Object.op_Implicit((Object)(object)character.CharMoveBlockCollider) && !flag)
				{
					((Collider)character.CharMoveBlockCollider).enabled = true;
				}
				CustomEffectsPlugin.Log.LogMessage((object)("[Banish] t=" + Time.time.ToString("F2") + " " + character.Name + (died ? " died while banished - restored as a body." : " returned.")));
			}
			finally
			{
				Banished.Remove(uid);
			}
		}
	}
	[HarmonyPatch(typeof(StatusEffectManager), "AddStatusEffect", new Type[]
	{
		typeof(StatusEffect),
		typeof(Character),
		typeof(string[])
	})]
	public class StatusEffectManager_AddStatusEffect
	{
		private static readonly FieldRef<StatusEffectManager, Character> ManagerCharacter = AccessTools.FieldRefAccess<StatusEffectManager, Character>("m_character");

		private static bool s_loggedOnce;

		[HarmonyPostfix]
		public static void Postfix(StatusEffectManager __instance, StatusEffect __result)
		{
			if ((Object)(object)__result == (Object)null)
			{
				return;
			}
			BanishEffect componentInChildren = ((Component)__result).GetComponentInChildren<BanishEffect>(true);
			if (!Object.op_Implicit((Object)(object)componentInChildren))
			{
				return;
			}
			Character val = ManagerCharacter.Invoke(__instance);
			if (Object.op_Implicit((Object)(object)val) && (((Component)__result).gameObject.activeInHierarchy || !BanishEffect.IsLocallyControlled(val)))
			{
				if (!s_loggedOnce)
				{
					s_loggedOnce = true;
					CustomEffectsPlugin.Log.LogMessage((object)"SL_Banish: status-arrival patch is live - banish now runs on every client.");
				}
				BanishEffect.BeginBanish(val, componentInChildren.Duration, ((Effect)componentInChildren).Delay, __result);
			}
		}
	}
	[HarmonyPatch(typeof(Character), "AnimSound")]
	public class Character_AnimSound
	{
		[HarmonyPrefix]
		public static bool Prefix(Character __instance)
		{
			return !BanishEffect.IsBanished(__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "AnimSoundRandom")]
	public class Character_AnimSoundRandom
	{
		[HarmonyPrefix]
		public static bool Prefix(Character __instance)
		{
			return !BanishEffect.IsBanished(__instance);
		}
	}
	public class SL_LearnSkillHidden : SL_Effect, ICustomModel
	{
		public int SkillID;

		public Type SLTemplateModel => typeof(SL_LearnSkillHidden);

		public Type GameModel => typeof(HiddenLearnSkillEffect);

		public override void ApplyToComponent<T>(T component)
		{
			HiddenLearnSkillEffect hiddenLearnSkillEffect = component as HiddenLearnSkillEffect;
			Item itemPrefab = ResourcesPrefabManager.Instance.GetItemPrefab(SkillID);
			Skill val = (Skill)(object)((itemPrefab is Skill) ? itemPrefab : null);
			if (val != null)
			{
				hiddenLearnSkillEffect.LearntSkill = val;
				HiddenSkills.Register(SkillID);
			}
			else
			{
				CustomEffectsPlugin.Log.LogWarning((object)("SL_LearnSkillHidden: no skill found with ID " + SkillID + " - nothing will be taught."));
			}
		}

		public override void SerializeEffect<T>(T effect)
		{
			SkillID = ((Item)((effect as HiddenLearnSkillEffect).LearntSkill?)).ItemID ?? (-1);
		}
	}
	public static class HiddenSkills
	{
		private static readonly HashSet<int> IDs = new HashSet<int>();

		public static bool Any => IDs.Count > 0;

		public static void Register(int skillID)
		{
			if (IDs.Add(skillID))
			{
				CustomEffectsPlugin.Log.LogMessage((object)("SL_LearnSkillHidden: hiding skill " + skillID + " from the skill menu."));
			}
		}

		public static bool IsHidden(int skillID)
		{
			if (IDs.Count > 0)
			{
				return IDs.Contains(skillID);
			}
			return false;
		}
	}
	public class HiddenLearnSkillEffect : Effect
	{
		public Skill LearntSkill;

		protected override void ActivateLocally(Character _affectedCharacter, object[] _infos)
		{
			if (Object.op_Implicit((Object)(object)_affectedCharacter) && Object.op_Implicit((Object)(object)LearntSkill) && _affectedCharacter.IsLocalPlayer && !((CharacterKnowledge)_affectedCharacter.Inventory.SkillKnowledge).IsItemLearned((Item)(object)LearntSkill))
			{
				_affectedCharacter.Inventory.TryUnlockSkill(LearntSkill);
				CustomEffectsPlugin.Log.LogMessage((object)("SL_LearnSkillHidden: taught " + ((Item)LearntSkill).Name + " (" + ((Item)LearntSkill).ItemID + ") to " + _affectedCharacter.Name + ", hidden from the menu."));
			}
		}
	}
	[HarmonyPatch(typeof(CharacterSkillKnowledge), "AddItem")]
	public class CharacterSkillKnowledge_AddItem
	{
		private static readonly FieldRef<CharacterSkillKnowledge, List<string>> ActiveUIDs = AccessTools.FieldRefAccess<CharacterSkillKnowledge, List<string>>("m_activeSkillUIDs");

		private static readonly FieldRef<CharacterSkillKnowledge, List<string>> PassiveUIDs = AccessTools.FieldRefAccess<CharacterSkillKnowledge, List<string>>("m_passiveSkillUIDs");

		private static readonly FieldRef<CharacterSkillKnowledge, List<string>> CosmeticUIDs = AccessTools.FieldRefAccess<CharacterSkillKnowledge, List<string>>("m_cosmeticSkillUIDs");

		[HarmonyPrefix]
		public static void Prefix(Item _item)
		{
			if (HiddenSkills.Any)
			{
				Skill val = (Skill)(object)((_item is Skill) ? _item : null);
				if (Object.op_Implicit((Object)(object)val) && HiddenSkills.IsHidden(((Item)val).ItemID))
				{
					val.IgnoreLearnNotification = true;
				}
			}
		}

		[HarmonyPostfix]
		public static void Postfix(CharacterSkillKnowledge __instance, Item _item)
		{
			if (HiddenSkills.Any && Object.op_Implicit((Object)(object)_item))
			{
				Skill val = (Skill)(object)((_item is Skill) ? _item : null);
				if (Object.op_Implicit((Object)(object)val) && HiddenSkills.IsHidden(((Item)val).ItemID))
				{
					string uID = _item.UID;
					ActiveUIDs.Invoke(__instance).Remove(uID);
					PassiveUIDs.Invoke(__instance).Remove(uID);
					CosmeticUIDs.Invoke(__instance).Remove(uID);
				}
			}
		}
	}
	public class SL_PlayTimedSound : SL_Effect, ICustomModel
	{
		public Sounds[] Sounds;

		public bool Follow;

		public float MinPitch = 1f;

		public float MaxPitch = 1f;

		public float StopAfter = 5f;

		public float FadeIn;

		public float FadeOut = 0.15f;

		public Type SLTemplateModel => typeof(SL_PlayTimedSound);

		public Type GameModel => typeof(PlayTimedSound);

		public override void ApplyToComponent<T>(T component)
		{
			PlayTimedSound playTimedSound = component as PlayTimedSound;
			playTimedSound.Sounds = (Sounds[])((Sounds == null) ? ((Array)new Sounds[0]) : ((Array)Sounds));
			playTimedSound.Follow = Follow;
			playTimedSound.MinPitch = MinPitch;
			playTimedSound.MaxPitch = MaxPitch;
			playTimedSound.StopAfter = StopAfter;
			playTimedSound.FadeIn = FadeIn;
			playTimedSound.FadeOut = FadeOut;
			if (playTimedSound.Sounds.Length == 0)
			{
				CustomEffectsPlugin.Log.LogWarning((object)"SL_PlayTimedSound: no Sounds were read - nothing will play. Each entry must be a <Sounds> element inside <Sounds>.");
			}
			else if (StopAfter <= 0f)
			{
				CustomEffectsPlugin.Log.LogWarning((object)("SL_PlayTimedSound (" + ((object)Unsafe.As<Sounds, Sounds>(ref playTimedSound.Sounds[0])/*cast due to .constrained prefix*/).ToString() + "): StopAfter is zero, so this behaves as SL_PlaySoundEffect - the sound ends with its clip or with the status. Set StopAfter to cut it earlier."));
			}
		}

		public override void SerializeEffect<T>(T effect)
		{
			PlayTimedSound playTimedSound = effect as PlayTimedSound;
			Sounds = playTimedSound.Sounds;
			Follow = playTimedSound.Follow;
			MinPitch = playTimedSound.MinPitch;
			MaxPitch = playTimedSound.MaxPitch;
			StopAfter = playTimedSound.StopAfter;
			FadeIn = playTimedSound.FadeIn;
			FadeOut = playTimedSound.FadeOut;
		}
	}
	public class PlayTimedSound : Effect
	{
		public Sounds[] Sounds = (Sounds[])(object)new Sounds[0];

		public bool Follow;

		public float MinPitch = 1f;

		public float MaxPitch = 1f;

		public float StopAfter = 5f;

		public float FadeIn;

		public float FadeOut = 0.15f;

		protected override void ActivateLocally(Character _affectedCharacter, object[] _infos)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Global.AudioManager == (Object)null || Sounds == null || Sounds.Length == 0)
			{
				return;
			}
			Sounds val = CharacterSoundManager.FindSound(Sounds);
			if ((int)val != 0)
			{
				SplitableSoundSource val2 = ((!Follow) ? Global.AudioManager.PlaySoundAtPosition(val, ((Component)this).transform, FadeIn, 1f, 1f, MinPitch, MaxPitch) : Global.AudioManager.PlaySoundAndFollow(val, ((Component)this).transform, FadeIn, 1f, 1f, MinPitch, MaxPitch));
				if ((Object)(object)val2 != (Object)null && StopAfter > 0f)
				{
					((MonoBehaviour)this).StartCoroutine(StopLater(val, val2));
				}
			}
		}

		private IEnumerator StopLater(Sounds sound, SplitableSoundSource source)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			yield return (object)new WaitForSeconds(StopAfter);
			if (!((Object)(object)source == (Object)null) && !((Object)(object)Global.AudioManager == (Object)null))
			{
				Global.AudioManager.StopSound(sound, source, FadeOut, false);
			}
		}
	}
	public class SL_PlayVFXOnChar : SL_Effect, ICustomModel
	{
		public VFXPrefabs VFXPrefab;

		public bool HitPos;

		public e_ParentMode ParentMode;

		public bool DontInstantiateNew;

		public Vector3 Offset = Vector3.zero;

		public PlayVFXOnChar.e_DetachMode Detach;

		public float DetachLifetime = 5f;

		public Type SLTemplateModel => typeof(SL_PlayVFXOnChar);

		public Type GameModel => typeof(PlayVFXOnChar);

		public override void ApplyToComponent<T>(T component)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			PlayVFXOnChar playVFXOnChar = component as PlayVFXOnChar;
			((PlayVFX)playVFXOnChar).HitPos = HitPos;
			((PlayVFX)playVFXOnChar).ParentMode = ParentMode;
			((PlayVFX)playVFXOnChar).VfxAlreadyInst = DontInstantiateNew;
			playVFXOnChar.Offset = Offset;
			playVFXOnChar.Detach = Detach;
			playVFXOnChar.DetachLifetime = DetachLifetime;
			GameObject vfxSystem = SL_PlayVFX.GetVfxSystem(VFXPrefab);
			if (vfxSystem == null)
			{
				CustomEffectsPlugin.Log.LogWarning((object)("SL_PlayVFXOnChar: no VFX prefab named " + ((object)Unsafe.As<VFXPrefabs, VFXPrefabs>(ref VFXPrefab)/*cast due to .constrained prefix*/).ToString() + " - nothing will play."));
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(vfxSystem);
			Object.DontDestroyOnLoad((Object)(object)val);
			val.SetActive(false);
			((PlayVFX)playVFXOnChar).VFX = val.GetComponent<VFXSystem>();
		}

		public override void SerializeEffect<T>(T effect)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			PlayVFXOnChar playVFXOnChar = effect as PlayVFXOnChar;
			HitPos = ((PlayVFX)playVFXOnChar).HitPos;
			ParentMode = ((PlayVFX)playVFXOnChar).ParentMode;
			DontInstantiateNew = ((PlayVFX)playVFXOnChar).VfxAlreadyInst;
			Offset = playVFXOnChar.Offset;
			Detach = playVFXOnChar.Detach;
			DetachLifetime = playVFXOnChar.DetachLifetime;
			VFXSystem vFX = ((PlayVFX)playVFXOnChar).VFX;
			if (vFX != null && (int)SL_PlayVFX.GetVFXSystemEnum(vFX) != 0)
			{
				VFXPrefab = SL_PlayVFX.GetVFXSystemEnum(vFX);
			}
		}
	}
	public class PlayVFXOnChar : PlayVFX
	{
		public enum e_DetachMode
		{
			None,
			Character,
			World
		}

		public Vector3 Offset = Vector3.zero;

		public e_DetachMode Detach;

		public float DetachLifetime = 5f;

		private static readonly FieldRef<PlayVFX, VFXSystem> StartVFXRef = AccessTools.FieldRefAccess<PlayVFX, VFXSystem>("m_startVFX");

		protected override void ActivateLocally(Character _affectedCharacter, object[] _infos)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			((PlayVFX)this).ActivateLocally(_affectedCharacter, _infos);
			if (!Object.op_Implicit((Object)(object)_affectedCharacter))
			{
				return;
			}
			VFXSystem val = StartVFXRef.Invoke((PlayVFX)(object)this);
			if (Object.op_Implicit((Object)(object)val))
			{
				((Component)val).transform.position = ((Component)_affectedCharacter).transform.position + Offset;
				if (Detach != e_DetachMode.None)
				{
					Transform val2 = ((Detach == e_DetachMode.Character) ? ((Component)_affectedCharacter).transform : null);
					((Component)val).transform.SetParent(val2, true);
					StartVFXRef.Invoke((PlayVFX)(object)this) = null;
					Object.Destroy((Object)(object)((Component)val).gameObject, DetachLifetime);
				}
			}
		}
	}
	public enum WeaponSlotOverride
	{
		Keep,
		MainHand,
		OffHand
	}
	public class SL_PlayVFXOnWeapon : SL_Effect, ICustomModel
	{
		public VFXPrefabs VFXPrefab;

		public bool HitPos;

		public e_ParentMode ParentMode;

		public bool DontInstantiateNew;

		public bool AllowSkinnedMesh = true;

		public WeaponSlotOverride Slot;

		public Type SLTemplateModel => typeof(SL_PlayVFXOnWeapon);

		public Type GameModel => typeof(PlayVFXOnWeapon);

		public override void ApplyToComponent<T>(T component)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			PlayVFXOnWeapon playVFXOnWeapon = component as PlayVFXOnWeapon;
			((PlayVFX)playVFXOnWeapon).HitPos = HitPos;
			((PlayVFX)playVFXOnWeapon).ParentMode = ParentMode;
			((PlayVFX)playVFXOnWeapon).VfxAlreadyInst = DontInstantiateNew;
			playVFXOnWeapon.AllowSkinnedMesh = AllowSkinnedMesh;
			playVFXOnWeapon.SlotOverride = Slot;
			GameObject vfxSystem = SL_PlayVFX.GetVfxSystem(VFXPrefab);
			if (vfxSystem != null)
			{
				GameObject val = Object.Instantiate<GameObject>(vfxSystem);
				Object.DontDestroyOnLoad((Object)(object)val);
				val.SetActive(false);
				((PlayVFX)playVFXOnWeapon).VFX = val.GetComponent<VFXSystem>();
				Configure(val);
			}
		}

		private void Configure(GameObject vfx)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			VFXParticlesOnWeapon[] componentsInChildren = vfx.GetComponentsInChildren<VFXParticlesOnWeapon>(true);
			foreach (VFXParticlesOnWeapon val in componentsInChildren)
			{
				if (AllowSkinnedMesh)
				{
					val.CheckSkinnedMeshRenderer = true;
				}
				if (Slot == WeaponSlotOverride.MainHand)
				{
					val.Slot = (WeaponSlot)0;
				}
				else if (Slot == WeaponSlotOverride.OffHand)
				{
					val.Slot = (WeaponSlot)1;
				}
			}
		}

		public override void SerializeEffect<T>(T effect)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			PlayVFXOnWeapon playVFXOnWeapon = effect as PlayVFXOnWeapon;
			HitPos = ((PlayVFX)playVFXOnWeapon).HitPos;
			ParentMode = ((PlayVFX)playVFXOnWeapon).ParentMode;
			DontInstantiateNew = ((PlayVFX)playVFXOnWeapon).VfxAlreadyInst;
			AllowSkinnedMesh = playVFXOnWeapon.AllowSkinnedMesh;
			Slot = playVFXOnWeapon.SlotOverride;
			VFXSystem vFX = ((PlayVFX)playVFXOnWeapon).VFX;
			if (vFX != null && (int)SL_PlayVFX.GetVFXSystemEnum(vFX) != 0)
			{
				VFXPrefab = SL_PlayVFX.GetVFXSystemEnum(vFX);
			}
		}
	}
	public class PlayVFXOnWeapon : PlayVFX
	{
		public bool AllowSkinnedMesh = true;

		public WeaponSlotOverride SlotOverride;
	}
	public class SL_PlayVFXPulsed : SL_Effect, ICustomModel
	{
		public VFXPrefabs VFXPrefab;

		public bool HitPos;

		public e_ParentMode ParentMode;

		public bool DontInstantiateNew;

		public List<float> PulseAt = new List<float>();

		public float PulseLength = 0.3f;

		public bool HardClear = true;

		public float StopAt;

		public Type SLTemplateModel => typeof(SL_PlayVFXPulsed);

		public Type GameModel => typeof(PlayVFXPulsed);

		public override void ApplyToComponent<T>(T component)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			PlayVFXPulsed playVFXPulsed = component as PlayVFXPulsed;
			((PlayVFX)playVFXPulsed).HitPos = HitPos;
			((PlayVFX)playVFXPulsed).ParentMode = ParentMode;
			((PlayVFX)playVFXPulsed).VfxAlreadyInst = DontInstantiateNew;
			playVFXPulsed.PulseLength = PulseLength;
			playVFXPulsed.HardClear = HardClear;
			playVFXPulsed.StopAt = StopAt;
			playVFXPulsed.PulseAt = ((PulseAt == null) ? new List<float>() : new List<float>(PulseAt));
			if (playVFXPulsed.PulseAt.Count == 0 && StopAt <= 0f)
			{
				CustomEffectsPlugin.Log.LogWarning((object)("SL_PlayVFXPulsed (" + ((object)Unsafe.As<VFXPrefabs, VFXPrefabs>(ref VFXPrefab)/*cast due to .constrained prefix*/).ToString() + "): no PulseAt times and no StopAt were read - it will behave as SL_PlayVFX. Each pulse must be a <float> element inside <PulseAt>."));
			}
			else
			{
				CustomEffectsPlugin.Log.LogMessage((object)("SL_PlayVFXPulsed (" + ((object)Unsafe.As<VFXPrefabs, VFXPrefabs>(ref VFXPrefab)/*cast due to .constrained prefix*/).ToString() + "): " + playVFXPulsed.PulseAt.Count + " pulse times, " + PulseLength + "s each, HardClear=" + HardClear + ((StopAt > 0f) ? (", stops at " + StopAt + "s") : "") + "."));
			}
			GameObject vfxSystem = SL_PlayVFX.GetVfxSystem(VFXPrefab);
			if (vfxSystem != null)
			{
				GameObject val = Object.Instantiate<GameObject>(vfxSystem);
				Object.DontDestroyOnLoad((Object)(object)val);
				val.SetActive(false);
				((PlayVFX)playVFXPulsed).VFX = val.GetComponent<VFXSystem>();
			}
		}

		public override void SerializeEffect<T>(T effect)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			PlayVFXPulsed playVFXPulsed = effect as PlayVFXPulsed;
			HitPos = ((PlayVFX)playVFXPulsed).HitPos;
			ParentMode = ((PlayVFX)playVFXPulsed).ParentMode;
			DontInstantiateNew = ((PlayVFX)playVFXPulsed).VfxAlreadyInst;
			PulseLength = playVFXPulsed.PulseLength;
			HardClear = playVFXPulsed.HardClear;
			StopAt = playVFXPulsed.StopAt;
			PulseAt = ((playVFXPulsed.PulseAt == null) ? new List<float>() : new List<float>(playVFXPulsed.PulseAt));
			VFXSystem vFX = ((PlayVFX)playVFXPulsed).VFX;
			if (vFX != null && (int)SL_PlayVFX.GetVFXSystemEnum(vFX) != 0)
			{
				VFXPrefab = SL_PlayVFX.GetVFXSystemEnum(vFX);
			}
		}
	}
	public class PlayVFXPulsed : PlayVFX
	{
		public List<float> PulseAt = new List<float>();

		public float PulseLength = 0.3f;

		public bool HardClear = true;

		public float StopAt;

		private static readonly FieldRef<PlayVFX, VFXSystem> StartVFXRef = AccessTools.FieldRefAccess<PlayVFX, VFXSystem>("m_startVFX");

		protected override void ActivateLocally(Character _affectedCharacter, object[] _infos)
		{
			((PlayVFX)this).ActivateLocally(_affectedCharacter, _infos);
			if ((PulseAt != null && PulseAt.Count > 0 && PulseLength > 0f) || StopAt > 0f)
			{
				((MonoBehaviour)this).StartCoroutine(PulseCoroutine());
			}
		}

		private IEnumerator PulseCoroutine()
		{
			VFXSystem vfx = StartVFXRef.Invoke((PlayVFX)(object)this);
			if (!Object.op_Implicit((Object)(object)vfx))
			{
				yield break;
			}
			ParticleSystem[] systems = ((Component)vfx).GetComponentsInChildren<ParticleSystem>(true);
			if (systems.Length == 0)
			{
				yield break;
			}
			ParticleSystemStopBehavior stopBehaviour = (ParticleSystemStopBehavior)(!HardClear);
			List<float> list = new List<float>(PulseAt);
			list.Sort();
			float elapsed = 0f;
			ParticleSystem[] array;
			foreach (float time in list)
			{
				if (StopAt > 0f && time >= StopAt)
				{
					break;
				}
				if (time < elapsed)
				{
					continue;
				}
				yield return (object)new WaitForSeconds(time - elapsed);
				elapsed = time;
				if (!Object.op_Implicit((Object)(object)vfx))
				{
					yield break;
				}
				array = systems;
				foreach (ParticleSystem val in array)
				{
					if (Object.op_Implicit((Object)(object)val))
					{
						val.Stop(false, stopBehaviour);
					}
				}
				yield return (object)new WaitForSeconds(PulseLength);
				elapsed += PulseLength;
				if (!Object.op_Implicit((Object)(object)vfx))
				{
					yield break;
				}
				array = systems;
				foreach (ParticleSystem val2 in array)
				{
					if (Object.op_Implicit((Object)(object)val2))
					{
						val2.Play(false);
					}
				}
			}
			if (StopAt <= 0f)
			{
				yield break;
			}
			if (StopAt > elapsed)
			{
				yield return (object)new WaitForSeconds(StopAt - elapsed);
			}
			if (!Object.op_Implicit((Object)(object)vfx))
			{
				yield break;
			}
			array = systems;
			foreach (ParticleSystem val3 in array)
			{
				if (Object.op_Implicit((Object)(object)val3))
				{
					val3.Stop(false, stopBehaviour);
				}
			}
		}
	}
	public class SL_ReliableVisuals : SL_Effect, ICustomModel
	{
		public bool IncludeBlasts;

		public Type SLTemplateModel => typeof(SL_ReliableVisuals);

		public Type GameModel => typeof(ReliableVisuals);

		public override void ApplyToComponent<T>(T component)
		{
			(component as ReliableVisuals).IncludeBlasts = IncludeBlasts;
		}

		public override void SerializeEffect<T>(T effect)
		{
			IncludeBlasts = (effect as ReliableVisuals).IncludeBlasts;
		}
	}
	public class ReliableVisuals : Effect
	{
		public bool IncludeBlasts;

		internal static readonly HashSet<string> Kicked = new HashSet<string>();

		protected override void ActivateLocally(Character _affectedCharacter, object[] _infos)
		{
		}

		internal static bool IsLocallyControlled(Character c)
		{
			if (!Object.op_Implicit((Object)(object)c))
			{
				return false;
			}
			if (c.IsAI)
			{
				return PhotonNetwork.isMasterClient;
			}
			return c.IsLocalPlayer;
		}

		internal static bool IsVisual(Effect effect, bool includeBlasts)
		{
			if (effect is PlayVFX || effect is PlaySoundEffect || effect is PlayTimedSound)
			{
				return true;
			}
			if (includeBlasts && effect is ShootBlast)
			{
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(StatusEffectManager), "AddStatusEffect", new Type[]
	{
		typeof(StatusEffect),
		typeof(Character),
		typeof(string[])
	})]
	public class StatusEffectManager_AddStatusEffect_Visuals
	{
		private static readonly FieldRef<StatusEffectManager, Character> ManagerCharacter = AccessTools.FieldRefAccess<StatusEffectManager, Character>("m_character");

		private static bool s_loggedOnce;

		[HarmonyPostfix]
		public static void Postfix(StatusEffectManager __instance, StatusEffect __result)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__result == (Object)null)
			{
				return;
			}
			ReliableVisuals componentInChildren = ((Component)__result).GetComponentInChildren<ReliableVisuals>(true);
			if (!Object.op_Implicit((Object)(object)componentInChildren))
			{
				return;
			}
			Character val = ManagerCharacter.Invoke(__instance);
			if (!Object.op_Implicit((Object)(object)val) || ReliableVisuals.IsLocallyControlled(val) || !((Component)__result).gameObject.activeInHierarchy)
			{
				return;
			}
			string item = UID.op_Implicit(__result.UID);
			if (ReliableVisuals.Kicked.Add(item))
			{
				if (!s_loggedOnce)
				{
					s_loggedOnce = true;
					CustomEffectsPlugin.Log.LogMessage((object)"SL_ReliableVisuals: arrival patch is live - marked statuses now play on every client.");
				}
				if (Object.op_Implicit((Object)(object)CustomEffectsPlugin.Instance))
				{
					((MonoBehaviour)CustomEffectsPlugin.Instance).StartCoroutine(PlayNextFrame(componentInChildren, __result, val));
				}
			}
		}

		private static IEnumerator PlayNextFrame(ReliableVisuals marker, StatusEffect status, Character affected)
		{
			yield return null;
			Play(marker, status, affected);
		}

		private static void Play(ReliableVisuals marker, StatusEffect status, Character affected)
		{
			if (!Object.op_Implicit((Object)(object)marker) || !Object.op_Implicit((Object)(object)status) || !Object.op_Implicit((Object)(object)affected))
			{
				return;
			}
			int num = 0;
			int num2 = 0;
			Effect[] componentsInChildren = ((Component)status).GetComponentsInChildren<Effect>(true);
			foreach (Effect val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val) && ReliableVisuals.IsVisual(val, marker.IncludeBlasts))
				{
					try
					{
						val.Affect(affected);
						num++;
					}
					catch (Exception ex)
					{
						num2++;
						CustomEffectsPlugin.Log.LogWarning((object)("SL_ReliableVisuals: " + ((object)val).GetType().Name + " threw " + ex.GetType().Name + " - " + ex.Message));
					}
				}
			}
			CustomEffectsPlugin.Log.LogMessage((object)("SL_ReliableVisuals: played " + num + " visual effects for " + affected.Name + ((num2 > 0) ? (" (" + num2 + " FAILED)") : "") + " | isMaster=" + PhotonNetwork.isMasterClient + " isAI=" + affected.IsAI + " blasts=" + marker.IncludeBlasts));
		}
	}
	[HarmonyPatch(typeof(StatusEffectManager), "OnReceiveActivatedStatusEffects")]
	public class StatusEffectManager_OnReceiveActivatedStatusEffects
	{
		[HarmonyPrefix]
		public static bool Prefix(string _statusUID)
		{
			if (string.IsNullOrEmpty(_statusUID))
			{
				return true;
			}
			if (ReliableVisuals.Kicked.Remove(_statusUID))
			{
				CustomEffectsPlugin.Log.LogMessage((object)("SL_ReliableVisuals: suppressed a duplicate activation message for " + _statusUID));
				return false;
			}
			return true;
		}
	}
	public enum SkillTarget
	{
		Owner,
		AffectedCharacter
	}
	public class SL_UseSkill : SL_Effect, ICustomModel
	{
		public int SkillID = -1;

		public SkillTarget Target;

		public float MaxWait = 3f;

		public Type SLTemplateModel => typeof(SL_UseSkill);

		public Type GameModel => typeof(UseSkillEffect);

		public override void ApplyToComponent<T>(T component)
		{
			UseSkillEffect obj = component as UseSkillEffect;
			obj.SkillID = SkillID;
			obj.Target = Target;
			obj.MaxWait = MaxWait;
		}

		public override void SerializeEffect<T>(T effect)
		{
			UseSkillEffect useSkillEffect = effect as UseSkillEffect;
			SkillID = useSkillEffect.SkillID;
			Target = useSkillEffect.Target;
			MaxWait = useSkillEffect.MaxWait;
		}
	}
	public class UseSkillEffect : Effect
	{
		public int SkillID = -1;

		public SkillTarget Target;

		public float MaxWait = 3f;

		private static bool IsFree(Character c)
		{
			if (Object.op_Implicit((Object)(object)c) && c.InLocomotion && c.NextIsLocomotion && !c.IsCasting && !c.IsHandlingBag)
			{
				return !c.IsHandlingWeapon;
			}
			return false;
		}

		protected override void ActivateLocally(Character _affectedCharacter, object[] _infos)
		{
			Character val = (((Object)(object)base.m_parentSynchronizer != (Object)null) ? base.m_parentSynchronizer.OwnerCharacter : null);
			Character val2 = ((Target == SkillTarget.Owner) ? val : _affectedCharacter);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				CustomEffectsPlugin.Log.LogWarning((object)("SL_UseSkill: no " + Target.ToString() + " character to cast skill " + SkillID + " - skipping."));
			}
			else if (IsLocallyControlled(val2))
			{
				Item itemFromItemID = ((CharacterKnowledge)val2.Inventory.SkillKnowledge).GetItemFromItemID(SkillID);
				Skill val3 = (Skill)(object)((itemFromItemID is Skill) ? itemFromItemID : null);
				if (!Object.op_Implicit((Object)(object)val3))
				{
					CustomEffectsPlugin.Log.LogWarning((object)("SL_UseSkill: " + val2.Name + " does not know skill " + SkillID + " - nothing cast. Teach it first (see SL_LearnSkillHidden) or use a skill the character knows."));
				}
				else
				{
					((MonoBehaviour)CustomEffectsPlugin.Instance).StartCoroutine(CastWhenFree(val2, val3));
				}
			}
		}

		private static bool IsLocallyControlled(Character c)
		{
			if (!Object.op_Implicit((Object)(object)c))
			{
				return false;
			}
			if (c.IsAI)
			{
				return PhotonNetwork.isMasterClient;
			}
			return c.IsLocalPlayer;
		}

		private IEnumerator CastWhenFree(Character caster, Skill skill)
		{
			float waited = 0f;
			while (Object.op_Implicit((Object)(object)caster) && !IsFree(caster) && waited < MaxWait)
			{
				waited += Time.deltaTime;
				yield return null;
			}
			if (Object.op_Implicit((Object)(object)caster) && Object.op_Implicit((Object)(object)skill))
			{
				if (!IsFree(caster))
				{
					CustomEffectsPlugin.Log.LogWarning((object)("SL_UseSkill: " + caster.Name + " never became free within " + MaxWait + "s (InLocomotion=" + caster.InLocomotion + " NextIsLocomotion=" + caster.NextIsLocomotion + " IsCasting=" + caster.IsCasting + " HandlingBag=" + caster.IsHandlingBag + " HandlingWeapon=" + caster.IsHandlingWeapon + ") - skill " + SkillID + " not cast."));
				}
				else
				{
					((Item)skill).TryQuickSlotUse();
				}
			}
		}
	}
}