Decompiled source of ZenBossStone v0.2.3

plugins\ZenBossStone.dll

Decompiled 5 days 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.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using JetBrains.Annotations;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using Zen.Config;
using Zen.Lib;
using Zen.Logging;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ZenBossStone")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ZenBossStone")]
[assembly: AssemblyCopyright("Copyright \ufffd  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 ZenBossStone
{
	public static class Commands
	{
		public static void RegisterRPC()
		{
			ZRoutedRpc.instance.Register<string>("RPC_BossStoneReset", (Action<long, string>)RPC_BossStoneReset);
			ZRoutedRpc.instance.Register<string>("RPC_BossStoneResponse", (Action<long, string>)RPC_BossStoneResponse);
		}

		public static void Init()
		{
			ZenMod<Plugin>.Terminal.CreateCommand("Reset", "Remove all trophies from ZDO data on Boss Stones and all player private keys for stones. Must be standing near them for it to work.", true, (Action<string[]>)delegate(string[] args)
			{
				if (args.Length <= 1)
				{
					Console.instance.Print("Syntax: " + args[0] + " playerName");
				}
				else
				{
					string text = args[1];
					ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "RPC_BossStoneReset", new object[1] { text });
				}
			});
		}

		private static void RPC_BossStoneReset(long sender, string playerName)
		{
			if (!string.Equals(playerName, Player.m_localPlayer.GetPlayerName(), StringComparison.CurrentCultureIgnoreCase))
			{
				return;
			}
			Log.Warning((object)$"Reset boss stones command sent from {sender} to {playerName}", (ushort)0);
			StringBuilder stringBuilder = new StringBuilder();
			BossStone[] array = Object.FindObjectsByType<BossStone>((FindObjectsSortMode)0);
			Player localPlayer = Player.m_localPlayer;
			stringBuilder.AppendLine(string.Format("{0} count: {1}", "BossStone", array.Length));
			if (((Character)localPlayer).InGodMode())
			{
				BossStone[] array2 = array;
				foreach (BossStone val in array2)
				{
					val.m_activeEffect.SetActive(false);
					ZNetView nview = val.m_itemStand.m_nview;
					nview.ClaimOwnership();
					nview.GetZDO().Set(ZDOVars.s_item, string.Empty);
					stringBuilder.AppendLine("Global ZDO Reset: " + Utils.GetPrefabName(((Object)val).name));
				}
			}
			else
			{
				stringBuilder.AppendLine("BossStone ZDO data not reset, " + playerName + " is not in God mode.");
			}
			stringBuilder.AppendLine("Remove private player keys");
			ItemStand[] array3 = Resources.FindObjectsOfTypeAll<ItemStand>();
			foreach (ItemStand val2 in array3)
			{
				if (Object.op_Implicit((Object)(object)val2.m_guardianPower))
				{
					string name = ((Object)val2.m_guardianPower).name;
					((Humanoid)localPlayer).RemoveUniqueKey(name);
					stringBuilder.AppendLine("Removed: " + name);
				}
			}
			stringBuilder.AppendLine("Player guardian power reset");
			PlayerExt.RemoveGuardianPower(localPlayer);
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "RPC_BossStoneResponse", new object[1] { stringBuilder.ToString() });
		}

		private static void RPC_BossStoneResponse(long sender, string message)
		{
			Console.instance.Print(message);
		}

		public static void UnregisterRPC()
		{
			ZRoutedRpcExt.Unregister(ZRoutedRpc.instance, "RPC_BossStoneReset");
			ZRoutedRpcExt.Unregister(ZRoutedRpc.instance, "RPC_BossStoneResponse");
		}
	}
	[HarmonyPatch]
	internal static class PatchBossStone
	{
		[HarmonyPatch(typeof(BossStone), "DelayedAttachEffects_Step3")]
		private static class BossStone_DelayedAttachEffects_Step3
		{
			[UsedImplicitly]
			private static void Prefix(BossStone __instance)
			{
				ZNetView.m_forceDisableInit = true;
			}

			[UsedImplicitly]
			private static void Postfix(BossStone __instance)
			{
				ZNetView.m_forceDisableInit = false;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(BossStone), "Start")]
		private static void BossStone_Start(BossStone __instance)
		{
			if (Configs.SacrificeTrophyForBossLoot.Value)
			{
				SetupFX(__instance);
			}
		}

		private static void SetupFX(BossStone bossStone)
		{
			EffectList val = bossStone.GetBoss().m_deathEffects;
			EffectData[] effectPrefabs = val.m_effectPrefabs;
			Ragdoll val2 = default(Ragdoll);
			for (int i = 0; i < effectPrefabs.Length; i++)
			{
				if (effectPrefabs[i].m_prefab.TryGetComponent<Ragdoll>(ref val2))
				{
					val = val2.m_removeEffect;
					break;
				}
			}
			bossStone.m_activateStep3.m_effectPrefabs = CollectionExtensions.AddRangeToArray<EffectData>(bossStone.m_activateStep3.m_effectPrefabs, val.m_effectPrefabs);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(BossStone), "SetActivated")]
		private static void BossStone_SetActivated(BossStone __instance, ref bool triggerEffect)
		{
			triggerEffect = State.IsInvokingSacrifice;
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(BossStone), "DelayedAttachEffects_Step3")]
		private static IEnumerable<CodeInstruction> BossStone_DelayedAttachEffects_Step3_Transpile(IEnumerable<CodeInstruction> codes)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Player), "MessageAllInRange", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(PatchBossStone), "MessageAllInRange_Intercept", (Type[])null, (Type[])null);
			return Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)methodInfo2);
		}

		private static void MessageAllInRange_Intercept(Vector3 point, float range, MessageType type, string msg, Sprite? icon = null)
		{
			//IL_001b: 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_0011: Unknown result type (might be due to invalid IL or missing references)
			if (Configs.PerPlayerBossStones.Value)
			{
				((Character)Player.m_localPlayer).Message(type, msg, 0, (Sprite)null);
			}
			else
			{
				Player.MessageAllInRange(point, range, type, msg, icon);
			}
		}
	}
	public static class Configs
	{
		internal static readonly ConfigEntry<bool> AutosetWorldModifierPlayerEvents;

		internal static readonly ConfigEntry<bool> PerPlayerBossStones;

		internal static readonly ConfigEntry<bool> SacrificeTrophyForBossLoot;

		internal static readonly ConfigEntry<PowerEmotes> EmoteToReceivePower;

		internal static readonly ConfigEntry<float> BossTrophyWeight;

		internal static readonly ConfigEntry<int> BossTrophyMaxStackSize;

		internal static readonly ConfigEntry<bool> BossTrophyAutopickup;

		internal static readonly ConfigEntry<bool> BossTrophyNoTeleport;

		internal static readonly ConfigEntry<bool> BossTrophyOnePerPlayer;

		internal static readonly Dictionary<string, string> TrophyToBoss;

		internal const float LootSpawnDelay = 11.5f;

		static Configs()
		{
			TrophyToBoss = new Dictionary<string, string>();
			AutosetWorldModifierPlayerEvents = Config.Define<bool>(true, "General", "Autoset World Modifier PlayerEvents", true, "Automatically enable the world modifier PlayerEvents on startup. (If run on server)\nThis will enable Valheim's player based progression.\nIf you are unfamiliar with how this modifier works please read the wiki:\nhttps://valheim.fandom.com/wiki/Events#Player-based_requirements");
			PerPlayerBossStones = Config.Define<bool>(true, "General", "Per Player Boss Stones", true, "Each player sees their own version of reality.\nAny player standing in the Start Temple when a trophy is sacrificed will have the trophy hung\nin their reality as well.  Any player not standing in the Start Temple when a trophy is sacrificed will\nnot see the trophy in their reality.");
			SacrificeTrophyForBossLoot = Config.Define<bool>(true, "General", "Sacrifice Trophy For Boss Loot", true, "Sacrifice boss trophy at boss stones for boss loot instead of dropping the loot directly from the boss when they die.\nCan sacrifice multiple boss trophies for extra loot");
			EmoteToReceivePower = Config.Define<PowerEmotes>(false, "General", "Emote To Recieve Power", PowerEmotes.Flex, "Player emotes to recieve guardian power. Just looks cool. Set to None to disable");
			BossTrophyWeight = Config.Define<float>(true, "Trophy", "Boss Trophy Weight", 100f, Config.AcceptRange<float>(0f, 500f), "How much do boss trophies weigh? (Vanilla: 2)\r\nThe idea is that a boss trophy is a large heavy object that is no-teleport and uses an entire inventory slot.\r\nThis creates a dynamic adventure of hunting, killing, and returning the trophy to the start.\r\nYou may need to craft a cart, carve paths, and use ships to haul it. The journey is the adventure.");
			BossTrophyMaxStackSize = Config.Define<int>(true, "Trophy", "Boss Trophy Max Stack Size", 1, Config.AcceptRange<int>(1, 99), "Max stack size of boss trophies (Vanilla: 20)\r\nThe idea is that a boss trophy is a large heavy object that is no-teleport and uses an entire inventory slot.\r\nThis creates a dynamic adventure of hunting, killing, and returning the trophy to the start.\r\nYou may need to craft a cart, carve paths, and use ships to haul it. The journey is the adventure.");
			BossTrophyNoTeleport = Config.Define<bool>(true, "Trophy", "Boss Trophy No Teleport", true, "Boss trophies are no-teleport? (Vanilla: false)\r\nThe idea is that a boss trophy is a large heavy object that is no-teleport and uses an entire inventory slot.\r\nThis creates a dynamic adventure of hunting, killing, and returning the trophy to the start.\r\nYou may need to craft a cart, carve paths, and use ships to haul it. The journey is the adventure.");
			BossTrophyAutopickup = Config.Define<bool>(true, "Trophy", "Boss Trophy Autopickup", false, "Autopickup boss trophies? (Vanilla: true)\r\nNote: Vanilla's globalkey: playerevents uses boss trophy pickup as a way of checking progression.\r\nIf you pickup a trophy by accident you will be flagged for that boss's progression and raids.");
			BossTrophyOnePerPlayer = Config.Define<bool>(true, "Trophy", "Boss Trophy One Per Player", false, "One boss trophy drops per player logged into the server when a boss dies? (Vanilla: false)");
		}

		internal static void BuildTrophyToBossLookup()
		{
			Log.Info((object)"Building Trophy To Boss Lookup", (ushort)0);
			ItemStand[] source = (from stone in Resources.FindObjectsOfTypeAll<BossStone>()
				where !((Behaviour)stone).isActiveAndEnabled
				select stone into s
				select ((Component)s).GetComponentInChildren<ItemStand>()).ToArray();
			IEnumerable<Character> enumerable = from c in Resources.FindObjectsOfTypeAll<Character>()
				where c.m_boss && !((Behaviour)c).isActiveAndEnabled
				select c;
			TrophyToBoss.Clear();
			CharacterDrop val = default(CharacterDrop);
			foreach (Character item in enumerable)
			{
				Log.Info((object)("Processing: " + ((Object)item).name), (ushort)0);
				if (!((Component)item).TryGetComponent<CharacterDrop>(ref val))
				{
					Log.Info((object)(((Object)item).name + " - No loot drops found"), (ushort)0);
					continue;
				}
				ItemDrop trophy = val.m_drops.Select((Drop d) => d.m_prefab.GetComponent<ItemDrop>()).FirstOrDefault((Func<ItemDrop, bool>)((ItemDrop item) => ItemDataExt.IsItemType(item.m_itemData, (ItemType)13)));
				if (trophy != null && Object.op_Implicit((Object)(object)((IEnumerable<ItemStand>)source).FirstOrDefault((Func<ItemStand, bool>)((ItemStand stand) => stand.IsSupported(trophy.m_itemData)))))
				{
					TrophyToBoss[((Object)trophy).name] = ((Object)item).name;
				}
			}
			foreach (KeyValuePair<string, string> item2 in TrophyToBoss)
			{
				Log.Info((object)(item2.Key + ": " + item2.Value), (ushort)0);
			}
		}

		internal static void SetupBossTrophyPrefabs()
		{
			Log.Info((object)"Setup Boss Trophy Prefabs", (ushort)0);
			Log.Info((object)$"Autopickup: {BossTrophyAutopickup.Value}", (ushort)0);
			Log.Info((object)$"NoTeleport: {BossTrophyNoTeleport.Value}", (ushort)0);
			Log.Info((object)$"Weight: {BossTrophyWeight.Value}", (ushort)0);
			Log.Info((object)$"MaxStackSize: {BossTrophyMaxStackSize.Value}", (ushort)0);
			GameObject val = default(GameObject);
			foreach (string key in TrophyToBoss.Keys)
			{
				if (!ObjectDB.instance.TryGetItemPrefab(key, ref val))
				{
					throw new Exception("Trophy missing from ObjectDB: " + key);
				}
				Log.Info((object)("- " + key), (ushort)0);
				ItemDrop component = val.GetComponent<ItemDrop>();
				component.m_autoPickup = BossTrophyAutopickup.Value;
				component.m_itemData.m_shared.m_teleportable = !BossTrophyNoTeleport.Value;
				component.m_itemData.m_shared.m_weight = BossTrophyWeight.Value;
				component.m_itemData.m_shared.m_maxStackSize = BossTrophyMaxStackSize.Value;
			}
		}
	}
	internal static class Extensions
	{
		public static bool IsWorldBoss(this Character character)
		{
			if (character.IsBoss())
			{
				return Configs.TrophyToBoss.Values.Contains(character.m_nview.GetPrefabName());
			}
			return false;
		}

		public static BossStone GetBossStone(this ItemStand itemStand)
		{
			return ((Component)itemStand).GetComponentInParent<BossStone>();
		}

		public static void InvokeSacrifice(this BossStone bossStone)
		{
			State.BroadcastSacrifice(bossStone);
			if (Configs.SacrificeTrophyForBossLoot.Value)
			{
				Timing.Delay(11.5f, (Action)bossStone.SpawnLoot);
			}
		}

		public static Character GetBoss(this BossStone bossStone)
		{
			ItemDrop trophy = bossStone.GetTrophy();
			return ZNetScene.instance.GetPrefab(Configs.TrophyToBoss[ItemDataExt.GetPrefabName(trophy)]).GetComponent<Character>();
		}

		public static ItemDrop GetTrophy(this BossStone bossStone)
		{
			return bossStone.m_itemStand.m_supportedItems[0];
		}

		public static bool IsBossStoneActive(this ItemStand itemStand, Player? player)
		{
			if (!ItemStandExt.IsBossStone(itemStand))
			{
				return false;
			}
			if (!ZenMod<Plugin>.Initialized)
			{
				return false;
			}
			if (!Configs.PerPlayerBossStones.Value)
			{
				return itemStand.HaveAttachment();
			}
			if (Object.op_Implicit((Object)(object)player))
			{
				return ((Humanoid)player).HaveUniqueKey(((Object)itemStand.m_guardianPower).name);
			}
			return false;
		}

		public static bool IsNear(this Player player, BossStone stone)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (Location.GetLocation(((Component)stone).transform.position, true).IsInside(((Component)player).transform.position, 0f, false))
			{
				return true;
			}
			Log.Info((object)("Outside area: " + player.GetPlayerName()), (ushort)0);
			return false;
		}

		public static void ActivateBossStone(this Player player, BossStone bossStone)
		{
			string name = ((Object)bossStone.m_itemStand.m_guardianPower).name;
			if (!((Humanoid)player).HaveUniqueKey(name))
			{
				((Humanoid)player).AddUniqueKey(name);
			}
		}

		public static ItemData? FindTrophyIn(this ItemStand itemStand, Inventory inventory)
		{
			string prefabName = ItemDataExt.GetPrefabName(itemStand.m_supportedItems[0]);
			return inventory.GetItem(prefabName, -1, true);
		}

		private static void SpawnLoot(this BossStone bossStone)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: 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_014a: 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_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: 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_0162: 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)
			Character boss = bossStone.GetBoss();
			Log.Info((object)("Spawn rewards for " + ((Object)boss).name), (ushort)0);
			CharacterDrop component = ((Component)boss).GetComponent<CharacterDrop>();
			if (Configs.BossTrophyOnePerPlayer.Value)
			{
				foreach (Drop drop in component.m_drops)
				{
					drop.m_onePerPlayer = false;
				}
			}
			Character character = component.m_character;
			component.m_character = boss;
			((Component)boss).transform.position = ((Component)bossStone).transform.position;
			List<KeyValuePair<GameObject, int>> source = component.GenerateDropList();
			component.m_character = character;
			source = source.Where((KeyValuePair<GameObject, int> entry) => !Configs.TrophyToBoss.ContainsKey(((Object)entry.Key).name)).ToList();
			foreach (KeyValuePair<GameObject, int> item in source)
			{
				Log.Info((object)$"{((Object)item.Key).name} = {item.Value}", (ushort)0);
			}
			Transform transform = ((Component)bossStone.m_itemStand).transform;
			Vector3 position = transform.position;
			position.y = ((Component)Player.m_localPlayer).transform.position.y + 0.5f;
			Vector3 val = position;
			Vector3 val2 = transform.forward * 3f;
			CharacterDrop.DropItems(source, val + val2, 0.25f);
		}
	}
	[HarmonyPatch]
	public static class PatchItemStand
	{
		[HarmonyTranspiler]
		[HarmonyPatch(typeof(ItemStand), "Awake")]
		private static IEnumerable<CodeInstruction> ItemStand_Awake_Transpile(IEnumerable<CodeInstruction> codes)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(MonoBehaviour), "InvokeRepeating", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(PatchItemStand), "ItemStandAwake_InvokeRepeating_Intercept", (Type[])null, (Type[])null);
			return Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)methodInfo2);
		}

		private static void ItemStandAwake_InvokeRepeating_Intercept(ItemStand itemStand, string methodName, float time, float repeatRate)
		{
			if (ItemStandExt.IsBossStone(itemStand) && Configs.PerPlayerBossStones.Value && !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				repeatRate = 1f;
			}
			((MonoBehaviour)itemStand).InvokeRepeating(methodName, time, repeatRate);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ItemStand), "GetHoverText")]
		private static void ItemStand_GetHoverText(ItemStand __instance, ref string __result)
		{
			if (!ItemStandExt.IsBossStone(__instance))
			{
				return;
			}
			__result = __result.Replace(StringExt.Localize("\n$guardianstone_hook_alreadyactive"), "");
			if (Configs.SacrificeTrophyForBossLoot.Value && __instance.IsBossStoneActive(Player.m_localPlayer))
			{
				if (State.IsInvokingSacrifice)
				{
					__result = "";
				}
				if (!(__result == "") && __instance.FindTrophyIn(((Humanoid)Player.m_localPlayer).GetInventory()) != null)
				{
					__result += StringExt.Localize("\n" + UI.PromptInteractAlt + " $prop_offerbowl_makeoffer");
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ItemStand), "Interact")]
		private static void ItemStand_Interact_Prefix(ItemStand __instance, bool alt, bool hold, ref bool __runOriginal, ref bool __result)
		{
			if (!ItemStandExt.IsBossStone(__instance) || !Configs.SacrificeTrophyForBossLoot.Value || !alt || hold)
			{
				return;
			}
			__runOriginal = false;
			if (!State.IsInvokingSacrifice)
			{
				Player localPlayer = Player.m_localPlayer;
				ItemData val = __instance.FindTrophyIn(((Humanoid)localPlayer).GetInventory());
				if (val == null)
				{
					((Character)localPlayer).Message((MessageType)2, "$piece_itemstand_missingitem", 0, (Sprite)null);
					Log.Info((object)"Missing trophy from inventory", (ushort)0);
					return;
				}
				((Humanoid)localPlayer).GetInventory().RemoveOneItem(val);
				Log.Info((object)("Trophy offered: " + ItemDataExt.GetPrefabName(val)), (ushort)0);
				__instance.GetBossStone().InvokeSacrifice();
				__result = true;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ItemStand), "Interact")]
		private static void ItemStand_Interact_Postfix(ItemStand __instance, bool alt, bool hold, bool __result)
		{
			if (ItemStandExt.IsBossStone(__instance) && __result && !(alt || hold) && !State.IsInvokingSacrifice && Configs.EmoteToReceivePower.Value != PowerEmotes.None)
			{
				string text = Configs.EmoteToReceivePower.Value.ToString().ToLower();
				Player.m_localPlayer.StartEmote(text, true);
				State.IsReceivingPower = true;
				Timing.Delay(__instance.m_powerActivationDelay, (Action)delegate
				{
					State.IsReceivingPower = false;
					((Character)Player.m_localPlayer).StopEmote();
				});
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ItemStand), "UseItem")]
		private static void ItemStand_UseItem_Prefix(ItemStand __instance, Humanoid user, ItemData item, ref bool __result, ref bool __runOriginal)
		{
			if (!ItemStandExt.IsBossStone(__instance) || !Configs.PerPlayerBossStones.Value)
			{
				return;
			}
			__runOriginal = false;
			__result = false;
			if (!State.IsInvokingSacrifice)
			{
				__result = true;
				if (__instance.CanAttach(item) && (!__instance.HaveAttachment() || Configs.SacrificeTrophyForBossLoot.Value))
				{
					((Humanoid)Player.m_localPlayer).GetInventory().RemoveOneItem(item);
					__instance.GetBossStone().InvokeSacrifice();
				}
				else
				{
					((Character)user).Message((MessageType)2, "$piece_itemstand_cantattach", 0, (Sprite)null);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ItemStand), "UseItem")]
		private static void ItemStand_UseItem_Postfix(ItemStand __instance, bool __result)
		{
			if (ItemStandExt.IsBossStone(__instance) && !Configs.PerPlayerBossStones.Value && __result)
			{
				__instance.GetBossStone().InvokeSacrifice();
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ItemStand), "UpdateVisual")]
		private static void ItemStand_UpdateVisual(ItemStand __instance, ref bool __runOriginal)
		{
			if (ItemStandExt.IsBossStone(__instance) && Configs.PerPlayerBossStones.Value)
			{
				Player localPlayer = Player.m_localPlayer;
				string name = ((Object)__instance.m_supportedItems[0]).name;
				__instance.SetVisualItem(__instance.IsBossStoneActive(localPlayer) ? name : string.Empty, 0, 1);
				__runOriginal = false;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ItemStand), "HaveAttachment")]
		private static void ItemStand_HaveAttachment(ItemStand __instance, ref bool __runOriginal, ref bool __result)
		{
			if (ItemStandExt.IsBossStone(__instance) && Configs.PerPlayerBossStones.Value)
			{
				Player localPlayer = Player.m_localPlayer;
				__result = __instance.IsBossStoneActive(localPlayer);
				__runOriginal = false;
			}
		}
	}
	[HarmonyPatch]
	public static class Patch
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		[HarmonyPriority(0)]
		private static void ZNetScene_Awake(ZNetScene __instance)
		{
			Log.Info((object)"Patching ZNetScene.Awake", (ushort)0);
			Configs.BuildTrophyToBossLookup();
			Configs.SetupBossTrophyPrefabs();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(CharacterDrop), "Start")]
		private static void CharacterDrop_Start(CharacterDrop __instance)
		{
			if (__instance.m_character.IsWorldBoss() && Configs.SacrificeTrophyForBossLoot.Value)
			{
				Drop val = ((IEnumerable<Drop>)__instance.m_drops).FirstOrDefault((Func<Drop, bool>)((Drop drop) => Configs.TrophyToBoss.ContainsKey(((Object)drop.m_prefab).name)));
				if (val == null)
				{
					Log.Warning((object)("Trophy not found in loot table: " + ((Object)__instance.m_character).name + ", can not sacrifice trophy for loot"), (ushort)0);
					return;
				}
				val.m_onePerPlayer = Configs.BossTrophyOnePerPlayer.Value;
				__instance.m_drops = new List<Drop>(1) { val };
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerController), "TakeInput")]
		private static void PlayerController_TakeInput(bool look, ref bool __result)
		{
			if (Configs.EmoteToReceivePower.Value != PowerEmotes.None)
			{
				__result = __result && (!State.IsReceivingPower || look);
			}
		}
	}
	public enum PowerEmotes
	{
		None = -1,
		Kneel = 15,
		Roar = 17,
		Laugh = 16,
		Challange = 2,
		Flex = 12,
		Bow = 8,
		Headbang = 14,
		Cheer = 3,
		Cower = 9,
		Toast = 21,
		Despair = 11,
		ThumbsUp = 5,
		Cry = 10,
		Shrug = 18
	}
	[BepInPlugin("ZenDragon.ZenBossStone", "ZenBossStone", "0.2.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal class Plugin : ZenMod<Plugin>
	{
		public const string PluginName = "ZenBossStone";

		public const string PluginVersion = "0.2.3";

		public const string PluginGUID = "ZenDragon.ZenBossStone";

		protected override void Setup()
		{
			((ZenMod)this).RunOnServer = true;
			Commands.Init();
			((ZenMod)this).ConfigSync += Configs.SetupBossTrophyPrefabs;
		}

		protected override void TitleScene(bool isFirstBoot)
		{
		}

		protected override void WorldStart()
		{
			if (Configs.TrophyToBoss.Count == 0)
			{
				Configs.BuildTrophyToBossLookup();
			}
			if (Configs.AutosetWorldModifierPlayerEvents.Value)
			{
				SetWorldModifierPlayerEvents();
			}
			State.RegisterRPC();
			Commands.RegisterRPC();
		}

		protected override void Shutdown()
		{
			State.UnregisterRPC();
			Commands.UnregisterRPC();
		}

		private static void SetWorldModifierPlayerEvents()
		{
			if (ZNet.instance.IsServer())
			{
				ZoneSystem.instance.SetGlobalKey((GlobalKeys)12);
				Log.Message((object)"World modifier enabled: PlayerEvents", (ushort)0);
			}
		}
	}
	public static class State
	{
		public static bool IsReceivingPower;

		private static readonly HashSet<string> InvokingSacrifice = new HashSet<string>();

		public static bool IsInvokingSacrifice => InvokingSacrifice.Count > 0;

		private static void SetInvokingSacrifice(BossStone bossStone)
		{
			string prefabName = Utils.GetPrefabName(((Object)bossStone).name);
			InvokingSacrifice.Add(prefabName);
			((MonoBehaviour)ZenMod<Plugin>.Instance).StartCoroutine(Finish());
			IEnumerator Finish()
			{
				yield return (object)new WaitForSeconds(11.5f);
				InvokingSacrifice.Remove(prefabName);
			}
		}

		public static void RegisterRPC()
		{
			ZRoutedRpc.instance.Register<ZDOID>("RPC_BossStoneSacrifice", (Action<long, ZDOID>)RPC_BossStoneSacrifice);
		}

		public static void UnregisterRPC()
		{
			ZRoutedRpcExt.Unregister(ZRoutedRpc.instance, "RPC_BossStoneSacrifice");
		}

		public static void BroadcastSacrifice(BossStone bossStone)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			ZDO zDO = bossStone.m_itemStand.m_nview.GetZDO();
			ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "RPC_BossStoneSacrifice", new object[1] { zDO.m_uid });
		}

		private static void RPC_BossStoneSacrifice(long sender, ZDOID itemStandID)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = ZNetScene.instance.FindInstance(itemStandID);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			BossStone componentInParent = val.GetComponentInParent<BossStone>();
			Player localPlayer = Player.m_localPlayer;
			if (Configs.SacrificeTrophyForBossLoot.Value)
			{
				componentInParent.m_active = false;
			}
			if (localPlayer.IsNear(componentInParent))
			{
				if (Configs.PerPlayerBossStones.Value)
				{
					localPlayer.ActivateBossStone(componentInParent);
				}
				SetInvokingSacrifice(componentInParent);
			}
		}
	}
}