using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
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;
using Zen.Lib;
using Zen.Lib.Config;
[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 Init()
{
ZenMod<Plugin>.Terminal.CreateCommand("ResetBossStone", "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
{
BossStoneReset();
});
}
private static void BossStoneReset()
{
Logging<Plugin>.Warning((object)"Reset boss stones", 0);
BossStone[] array = Object.FindObjectsByType<BossStone>((FindObjectsInactive)0, (FindObjectsSortMode)0);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine(string.Format("{0} count: {1}", "BossStone", array.Length));
if (((Character)Player.m_localPlayer).InGodMode())
{
BossStone[] array2 = array;
foreach (BossStone val in array2)
{
if (!MathExt.InRange((MonoBehaviour)(object)val, (MonoBehaviour)(object)Player.m_localPlayer, 50f))
{
Logging<Plugin>.Warning((object)("Skipping BossStone, too far away: " + GameObjectExt.GetPrefabName(((Component)val).gameObject)), 0);
continue;
}
val.m_activeEffect.SetActive(false);
ZNetView val2 = val.m_itemStand?.m_nview;
if (Object.op_Implicit((Object)(object)val2))
{
val2.ClaimOwnership();
val2.GetZDO().Set(ZDOVars.s_item, string.Empty);
stringBuilder.AppendLine("Global ZDO Reset: " + GameObjectExt.GetPrefabName(((Component)val).gameObject));
}
}
}
else
{
stringBuilder.AppendLine("BossStone ZDO data not reset, not in God mode.");
}
stringBuilder.AppendLine("Remove private player keys");
ItemStand[] array3 = Resources.FindObjectsOfTypeAll<ItemStand>();
foreach (ItemStand val3 in array3)
{
if (Object.op_Implicit((Object)(object)val3.m_guardianPower))
{
string name = ((Object)val3.m_guardianPower).name;
if (((Humanoid)Player.m_localPlayer).RemoveUniqueKey(name))
{
stringBuilder.AppendLine("Removed: " + name);
}
}
}
PlayerExt.RemoveGuardianPower(Player.m_localPlayer);
stringBuilder.AppendLine("Player guardian power reset");
Console.instance.Print(stringBuilder.ToString());
}
}
[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;
}
[UsedImplicitly]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> codes)
{
MethodInfo methodInfo = AccessTools.Method(typeof(Player), "MessageAllInRange", (Type[])null, (Type[])null);
Action<Vector3, float, MessageType, string, Sprite> action = MessageAllInRangeIntercept;
return Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)action.Method);
static void MessageAllInRangeIntercept(Vector3 point, float range, MessageType type, string msg, Sprite? icon)
{
//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);
}
}
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(BossStone), "Start")]
private static void BossStone_Start(BossStone __instance)
{
if (Configs.SacrificeTrophyForBossLoot.Value)
{
SetupFX(__instance);
}
}
private static void SetupFX(BossStone bossStone)
{
Character boss = bossStone.GetBoss();
if (!Object.op_Implicit((Object)(object)boss))
{
return;
}
EffectList val = boss.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;
}
}
public enum PortalRestriction : byte
{
None,
WoodOnly,
WoodAndStone
}
internal static class Configs
{
public static readonly ConfigEntry<bool> AutoSetWorldModifierPlayerEvents;
public static readonly ConfigEntry<bool> PerPlayerBossStones;
public static readonly ConfigEntry<bool> SacrificeTrophyForBossLoot;
public static readonly ConfigEntry<int> SacrificeTrophyRange;
private static readonly ConfigEntry<StringList> SacrificeTrophyIgnoredBosses;
private static readonly ConfigEntry<bool> RemoveTrophyFromRaidReqs;
public static readonly ConfigEntry<PowerEmotes> EmoteToReceivePower;
public static readonly ConfigEntry<bool> BossTrophyOnePerPlayer;
private static readonly ConfigEntry<float> BossTrophyWeight;
private static readonly ConfigEntry<int> BossTrophyMaxStackSize;
private static readonly ConfigEntry<bool> BossTrophyAutoPickup;
public static readonly ConfigEntry<PortalRestriction> BossTrophyNoTeleport;
private const string EikthyrTrophy = "TrophyEikthyr";
private const int EikthyrTrophyMaxWeight = 200;
internal static readonly Dictionary<string, string> TrophyToBoss;
internal const float LootSpawnDelay = 12f;
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.\r\nAny player standing in the Start Temple when a trophy is sacrificed will have the trophy hung\r\nin their reality as well. Any player not standing in the Start Temple when a trophy is sacrificed will\r\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.\r\nCan sacrifice multiple boss trophies for extra loot.");
SacrificeTrophyRange = Config.Define<int>(true, "General", "Sacrifice Trophy Range", 25, Config.AcceptRange<int>(10, 50), "The search radius around the BossStone used to find players eligible for rewards after a trophy is sacrificed.");
SacrificeTrophyIgnoredBosses = Config.Define<StringList>(true, "General", "Sacrifice Trophy Ignored Bosses", StringList.Empty, "Comma sepearated list of bosses that will NOT drop their loot when the trophy is hung.\r\nInstead, they will drop their loot when you kill them.\r\n[logout required for changes to take effect]");
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", 500f, Config.AcceptRange<float>(0f, 2000f), string.Format("How much do boss trophies weigh? (Vanilla: 2)\r\nNOTE: Eikthyr's trophy will never weigh more than {0} because no cart yet.\r\n{1}", 200, "The 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.\r\n[logout required for changes to take effect]"));
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.\r\n[logout required for changes to take effect]");
BossTrophyNoTeleport = Config.Define<PortalRestriction>(true, "Trophy", "Boss Trophy No Teleport", PortalRestriction.WoodAndStone, string.Format("Boss trophies are no-teleport on these portal types (Vanilla: None)\r\n- {0}: No teleport in any portal type.\r\n- {1}: No teleport in wooden portals, but stone is allowed.\r\n- {2}: Allow teleport in any portal type.\r\n{3}", PortalRestriction.WoodAndStone, PortalRestriction.WoodOnly, PortalRestriction.None, "The 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.\r\n[logout required for changes to take effect]"));
RemoveTrophyFromRaidReqs = Config.Define<bool>(true, "Trophy", "Remove Trophy From Raid Requirements", true, "In vanilla when player based raids (PlayerEvents) is enabled it makes it possible to trigger raids by simply picking up the boss trophy.\r\nWhen this option is enabled then knowledge of the boss trophy will be ignored when calculating raid requirements.\r\n(Vanilla: false)");
BossTrophyAutoPickup = Config.Define<bool>(true, "Trophy", "Boss Trophy Autopickup", false, "Autopickup boss trophies? (Vanilla: true)\r\nNote: Vanilla's 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.\r\nHowever, if you enable the config option " + ((ConfigEntryBase)RemoveTrophyFromRaidReqs).Definition.Key + " then the boss trophy will be ignored.");
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)\r\nIn vanilla some boss loot drops one per player and other drops a flat amount.\r\nEikthyr for example drops 3x antlers. However, The Elder drops 1 swamp key per player.\r\nWith this enabled then when the boss dies they drop one trophy per player, but each trophy\r\nwill only drop 1 copy of it's loot, not one copy of loot per player.\r\nIf this is on:\r\nIf two people kill Eikthyr then you will have two trophies and 6x antlers.\r\nIf two people kill The Elder then you will have two trophies and 2x keys.\r\nIf this is off:\r\nIf two people kill Eikthyr then you will have one trophy and 3x antlers.\r\nIf two people kill The Elder then you will have one trophy and 2x keys.");
}
internal static void Init()
{
BuildTrophyToBossLookup();
SetupBossTrophyPrefabs();
if (RemoveTrophyFromRaidReqs.Value)
{
RemoveBossTrophyFromRaidRequirements();
}
}
private static void BuildTrophyToBossLookup()
{
Logging<Plugin>.Info((object)"Building Trophy To Boss Lookup", 0);
ItemStand[] source = (from stone in Resources.FindObjectsOfTypeAll<BossStone>()
where !((Behaviour)stone).isActiveAndEnabled
select ((Component)stone).GetComponentInChildren<ItemStand>() into stand
where Object.op_Implicit((Object)(object)stand)
select stand).ToArray();
IEnumerable<Character> enumerable = from c in Resources.FindObjectsOfTypeAll<Character>()
where c.m_boss && !((Behaviour)c).isActiveAndEnabled
select c;
List<string> list = new List<string>((IEnumerable<string>)SacrificeTrophyIgnoredBosses.Value) { "Hive" };
TrophyToBoss.Clear();
CharacterDrop val = default(CharacterDrop);
foreach (Character item in enumerable)
{
Logging<Plugin>.Info((object)("Processing: " + ((Object)item).name), 0);
if (list.Contains(((Object)item).name))
{
Logging<Plugin>.Info((object)("Skipping ignored boss: " + ((Object)item).name), 0);
continue;
}
if (!((Component)item).TryGetComponent<CharacterDrop>(ref val))
{
Logging<Plugin>.Info((object)(((Object)item).name + " - No loot drops found, skipping"), 0);
continue;
}
ItemDrop trophy = val.m_drops.Select((Drop d) => d.m_prefab.GetComponent<ItemDrop>()).FirstOrDefault((Func<ItemDrop, bool>)((ItemDrop item) => Object.op_Implicit((Object)(object)item) && ItemDataExt.IsItemType(item.m_itemData, (ItemType)13)));
string value;
if (!Object.op_Implicit((Object)(object)trophy))
{
Logging<Plugin>.Info((object)("No trophy found for boss: " + ((Object)item).name + ", skipping"), 0);
}
else if (!Object.op_Implicit((Object)(object)((IEnumerable<ItemStand>)source).FirstOrDefault((Func<ItemStand, bool>)((ItemStand stand) => stand.IsSupported(trophy.m_itemData)))))
{
Logging<Plugin>.Info((object)("Skipping " + ((Object)item).name + ": No boss stone item stand found that supports the trophy " + ((Object)trophy).name), 0);
}
else if (TrophyToBoss.TryGetValue(((Object)trophy).name, out value))
{
Logging<Plugin>.Warning((object)("Skipping " + ((Object)item).name + ": The trophy " + ((Object)trophy).name + " is already associated with " + value), 0);
}
else
{
TrophyToBoss.Add(((Object)trophy).name, ((Object)item).name);
}
}
Logging<Plugin>.Info((object)"Trophy To Boss Lookup:", 0);
foreach (KeyValuePair<string, string> item2 in TrophyToBoss)
{
Logging<Plugin>.Info((object)("- " + item2.Key + ": " + item2.Value), 0);
}
}
private static void SetupBossTrophyPrefabs()
{
Logging<Plugin>.Info((object)"Setup Boss Trophy Prefabs", 0);
Logging<Plugin>.Info((object)$"Autopickup: {BossTrophyAutoPickup.Value}", 0);
Logging<Plugin>.Info((object)$"NoTeleport: {BossTrophyNoTeleport.Value}", 0);
Logging<Plugin>.Info((object)$"Weight: {BossTrophyWeight.Value}", 0);
Logging<Plugin>.Info((object)$"MaxStackSize: {BossTrophyMaxStackSize.Value}", 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);
}
Logging<Plugin>.Info((object)("- " + key), 0);
ItemDrop component = val.GetComponent<ItemDrop>();
component.m_autoPickup = BossTrophyAutoPickup.Value;
component.m_autoDestroy = false;
component.m_itemData.m_shared.m_teleportable = BossTrophyNoTeleport.Value == PortalRestriction.None;
component.m_itemData.m_shared.m_weight = GetWeight(key);
component.m_itemData.m_shared.m_maxStackSize = BossTrophyMaxStackSize.Value;
}
static float GetWeight(string trophyName)
{
if (!(trophyName == "TrophyEikthyr"))
{
return BossTrophyWeight.Value;
}
return Mathf.Min(200f, BossTrophyWeight.Value);
}
}
private static void RemoveBossTrophyFromRaidRequirements()
{
foreach (RandomEvent @event in RandEventSystem.instance.m_events)
{
RemoveTrophy(@event.m_name, @event.m_altRequiredKnownItems);
RemoveTrophy(@event.m_name, @event.m_altRequiredNotKnownItems);
}
static void RemoveTrophy(string raidName, List<ItemDrop> list)
{
ItemDrop[] array = list.ToArray();
foreach (ItemDrop val in array)
{
string prefabName = ItemDataExt.GetPrefabName(val);
if (TrophyToBoss.ContainsKey(prefabName))
{
list.Remove(val);
Logging<Plugin>.Info((object)(raidName + ": Removed " + prefabName), 0);
}
}
}
}
}
public static class Extensions
{
[CompilerGenerated]
private sealed class <GetBossLoot>d__10 : IEnumerable<KeyValuePair<GameObject, int>>, IEnumerable, IEnumerator<KeyValuePair<GameObject, int>>, IDisposable, IEnumerator
{
private int <>1__state;
private KeyValuePair<GameObject, int> <>2__current;
private int <>l__initialThreadId;
private BossStone bossStone;
public BossStone <>3__bossStone;
private int numPlayersInRange;
public int <>3__numPlayersInRange;
private Character <bossPrefab>5__2;
private int <lvlMult>5__3;
private IEnumerator<Drop> <>7__wrap3;
KeyValuePair<GameObject, int> IEnumerator<KeyValuePair<GameObject, int>>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <GetBossLoot>d__10(int <>1__state)
{
this.<>1__state = <>1__state;
<>l__initialThreadId = Environment.CurrentManagedThreadId;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if (num == -3 || num == 1)
{
try
{
}
finally
{
<>m__Finally1();
}
}
<bossPrefab>5__2 = null;
<>7__wrap3 = null;
<>1__state = -2;
}
private bool MoveNext()
{
try
{
switch (<>1__state)
{
default:
return false;
case 0:
{
<>1__state = -1;
<bossPrefab>5__2 = bossStone.GetBoss();
if (!Object.op_Implicit((Object)(object)<bossPrefab>5__2))
{
return false;
}
Logging<Plugin>.Info((object)("Get loot for boss: " + ((Object)<bossPrefab>5__2).name), 0);
CharacterDrop component = ((Component)<bossPrefab>5__2).GetComponent<CharacterDrop>();
IEnumerable<Drop> enumerable = component.m_drops.BossLootExceptTrophy();
<lvlMult>5__3 = ((!Object.op_Implicit((Object)(object)component.m_character)) ? 1 : ((int)Mathf.Max(1f, Mathf.Pow(2f, (float)(component.m_character.GetLevel() - 1)))));
<>7__wrap3 = enumerable.GetEnumerator();
<>1__state = -3;
break;
}
case 1:
<>1__state = -3;
break;
}
while (<>7__wrap3.MoveNext())
{
Drop current = <>7__wrap3.Current;
if (!Object.op_Implicit((Object)(object)current.m_prefab))
{
Logging<Plugin>.Warning((object)("drop prefab is missing for boss loot item: " + ((Object)<bossPrefab>5__2).name), 0);
continue;
}
int num;
if (current.m_onePerPlayer && !Configs.BossTrophyOnePerPlayer.Value)
{
Logging<Plugin>.Info((object)"Drop one per player", 0);
num = numPlayersInRange;
}
else if (current.m_dontScale)
{
Logging<Plugin>.Info((object)"Don't scale loot", 0);
num = Random.Range(current.m_amountMin, current.m_amountMax);
}
else
{
Logging<Plugin>.Info((object)"Scaled loot", 0);
num = Game.instance.ScaleDrops(current.m_prefab, current.m_amountMin, current.m_amountMax);
}
if (current.m_levelMultiplier)
{
Logging<Plugin>.Info((object)$"Level multiplier: {<lvlMult>5__3}", 0);
num *= <lvlMult>5__3;
}
if (num > 100)
{
num = 100;
}
if (num <= 0)
{
Logging<Plugin>.Warning((object)$"Skipping invalid amount: {num}, {((Object)current.m_prefab).name}", 0);
continue;
}
Logging<Plugin>.Info((object)$"Loot drop: {((Object)current.m_prefab).name} x {num}", 0);
<>2__current = new KeyValuePair<GameObject, int>(current.m_prefab, num);
<>1__state = 1;
return true;
}
<>m__Finally1();
<>7__wrap3 = null;
return false;
}
catch
{
//try-fault
((IDisposable)this).Dispose();
throw;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
private void <>m__Finally1()
{
<>1__state = -1;
if (<>7__wrap3 != null)
{
<>7__wrap3.Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
[DebuggerHidden]
IEnumerator<KeyValuePair<GameObject, int>> IEnumerable<KeyValuePair<GameObject, int>>.GetEnumerator()
{
<GetBossLoot>d__10 <GetBossLoot>d__;
if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
{
<>1__state = 0;
<GetBossLoot>d__ = this;
}
else
{
<GetBossLoot>d__ = new <GetBossLoot>d__10(0);
}
<GetBossLoot>d__.bossStone = <>3__bossStone;
<GetBossLoot>d__.numPlayersInRange = <>3__numPlayersInRange;
return <GetBossLoot>d__;
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<KeyValuePair<GameObject, int>>)this).GetEnumerator();
}
}
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)
{
BossStone bossStone2 = bossStone;
Logging<Plugin>.Info((object)"Count the players in range:", 0);
int numPlayersInRange = Player.GetAllPlayers().Count((Player p) => p.IsNear(bossStone2));
State.BroadcastSacrifice(bossStone2);
if (Configs.SacrificeTrophyForBossLoot.Value)
{
Timing.Delay((MonoBehaviour)(object)bossStone2, 12f, (Action)delegate
{
bossStone2.SpawnLoot(numPlayersInRange);
});
}
}
public static Character? GetBoss(this BossStone bossStone)
{
ItemDrop trophy = bossStone.GetTrophy();
if (Object.op_Implicit((Object)(object)trophy) && Configs.TrophyToBoss.TryGetValue(ItemDataExt.GetPrefabName(trophy), out string value))
{
return GlobalStatic.GetPrefab<Character>(value, false);
}
Logging<Plugin>.Warning((object)("Unable to get boss from BossStone " + ((Object)bossStone).name), 0);
return null;
}
public static ItemDrop? GetTrophy(this BossStone bossStone)
{
if (Object.op_Implicit((Object)(object)bossStone.m_itemStand) && bossStone.m_itemStand.m_supportedItems.Count > 0)
{
return bossStone.m_itemStand.m_supportedItems[0];
}
Logging<Plugin>.Warning((object)("Trophy unavailable, ItemStand data is missing from BossStone " + ((Object)bossStone).name), 0);
return null;
}
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)
{
if (!Object.op_Implicit((Object)(object)player))
{
return false;
}
string text = (((Object)(object)player == (Object)(object)Player.m_localPlayer) ? "You are" : (player.GetPlayerName() + " is"));
if (MathExt.DistanceToXZ((MonoBehaviour)(object)player, (MonoBehaviour)(object)stone) <= (float)Configs.SacrificeTrophyRange.Value)
{
Logging<Plugin>.Info((object)(GameObjectExt.GetPrefabName(((Component)stone).gameObject) + ": " + text + " in range"), 0);
return true;
}
Logging<Plugin>.Info((object)(GameObjectExt.GetPrefabName(((Component)stone).gameObject) + ": " + text + " too far away"), 0);
return false;
}
public static void ActivateBossStone(this Player? player, BossStone bossStone)
{
if (Object.op_Implicit((Object)(object)player))
{
ItemStand itemStand = bossStone.m_itemStand;
object obj;
if (itemStand == null)
{
obj = null;
}
else
{
StatusEffect guardianPower = itemStand.m_guardianPower;
obj = ((guardianPower != null) ? ((Object)guardianPower).name : null);
}
string text = (string)obj;
if (text == null)
{
Logging<Plugin>.Error((object)("Guradian power or ItemStand is missing from BossStone " + ((Object)bossStone).name), (ushort)0);
}
else if (!((Humanoid)player).HaveUniqueKey(text))
{
((Humanoid)player).AddUniqueKey(text);
}
}
}
public static ItemData? FindTrophyIn(this ItemStand itemStand, Inventory inventory)
{
if (itemStand.m_supportedItems.Count == 0)
{
return null;
}
string prefabName = ItemDataExt.GetPrefabName(itemStand.m_supportedItems[0]);
return inventory.GetItem(prefabName, -1, true);
}
private static void SpawnLoot(this BossStone bossStone, int numPlayersInRange)
{
//IL_0013: 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_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: 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_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: 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_0052: 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)
IEnumerable<KeyValuePair<GameObject, int>> bossLoot = GetBossLoot(bossStone, numPlayersInRange);
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(bossLoot.ToList(), val + val2, 0.25f);
}
[IteratorStateMachine(typeof(<GetBossLoot>d__10))]
private static IEnumerable<KeyValuePair<GameObject, int>> GetBossLoot(BossStone bossStone, int numPlayersInRange)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <GetBossLoot>d__10(-2)
{
<>3__bossStone = bossStone,
<>3__numPlayersInRange = numPlayersInRange
};
}
internal static IEnumerable<Drop> BossLootExceptTrophy(this List<Drop> drops)
{
return drops.Where((Drop d) => d.m_chance >= 1f && Object.op_Implicit((Object)(object)d.m_prefab) && !Configs.TrophyToBoss.ContainsKey(((Object)d.m_prefab).name));
}
}
[HarmonyPatch]
internal static class PatchMisc
{
[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);
}
}
}
[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);
Action<ItemStand, string, float, float> action = ItemStandAwake_InvokeRepeating_Intercept;
return Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)action.Method);
}
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"), string.Empty);
if (Configs.SacrificeTrophyForBossLoot.Value && __instance.IsBossStoneActive(Player.m_localPlayer))
{
if (State.IsInvokingSacrifice)
{
__result = string.Empty;
}
if (!(__result == string.Empty) && __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);
Logging<Plugin>.Info((object)"Missing trophy from inventory", 0);
return;
}
((Humanoid)localPlayer).GetInventory().RemoveOneItem(val);
Logging<Plugin>.Info((object)("Trophy offered: " + ItemDataExt.GetPrefabName(val)), 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((MonoBehaviour)(object)__instance, __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;
int orientation = __instance.GetOrientation();
__instance.SetVisualItem(__instance.IsBossStoneActive(localPlayer) ? name : string.Empty, 0, 1, orientation);
__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 PatchCharacterDrop
{
[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)
{
val.m_onePerPlayer = Configs.BossTrophyOnePerPlayer.Value;
}
else
{
Logging<Plugin>.Warning((object)("Trophy not found in loot table: " + ((Object)__instance.m_character).name + ", can not sacrifice trophy for loot"), 0);
}
}
}
[HarmonyPrefix]
[HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")]
private static void CharacterDrop_GenerateDropList(CharacterDrop __instance)
{
if (Configs.SacrificeTrophyForBossLoot.Value && Object.op_Implicit((Object)(object)__instance.m_character) && __instance.m_character.IsWorldBoss())
{
List<Drop> drops = __instance.m_drops;
IEnumerable<Drop> source = drops.Except(drops.BossLootExceptTrophy());
__instance.m_drops = source.ToList();
}
}
}
[HarmonyPatch]
internal class PatchTeleport
{
private static bool CheckForTrophy(TeleportWorld instance)
{
if (Configs.BossTrophyNoTeleport.Value != PortalRestriction.WoodAndStone)
{
return instance.m_allowAllItems;
}
if (!Object.op_Implicit((Object)(object)Player.m_localPlayer))
{
return instance.m_allowAllItems;
}
Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
if (inventory == null)
{
return instance.m_allowAllItems;
}
if (!inventory.GetAllItems().Any((ItemData item) => Configs.TrophyToBoss.ContainsKey(ItemDataExt.GetPrefabName(item))))
{
return instance.m_allowAllItems;
}
return false;
}
private static IEnumerable<CodeInstruction> AllowAllItems_Transpiler(IEnumerable<CodeInstruction> codes)
{
FieldInfo from = AccessTools.Field(typeof(TeleportWorld), "m_allowAllItems");
Func<TeleportWorld, bool> to = CheckForTrophy;
return Transpilers.Manipulator(codes, (Func<CodeInstruction, bool>)IsMatch, (Action<CodeInstruction>)Replace);
bool IsMatch(CodeInstruction c)
{
if (c.opcode == OpCodes.Ldfld)
{
return (FieldInfo)c.operand == from;
}
return false;
}
void Replace(CodeInstruction c)
{
c.opcode = OpCodes.Call;
c.operand = to.Method;
}
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(TeleportWorld), "Teleport")]
private static IEnumerable<CodeInstruction> TeleportWorld_Teleport_Transpiler(IEnumerable<CodeInstruction> codes)
{
return AllowAllItems_Transpiler(codes);
}
[HarmonyTranspiler]
[HarmonyPatch(typeof(TeleportWorld), "UpdatePortal")]
private static IEnumerable<CodeInstruction> TeleportWorld_UpdatePortal_Transpiler(IEnumerable<CodeInstruction> codes)
{
return AllowAllItems_Transpiler(codes);
}
}
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.5.1")]
[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.5.1";
public const string PluginGUID = "ZenDragon.ZenBossStone";
protected override void Setup()
{
((ZenMod)this).RunOnServer = true;
Commands.Init();
base.ConfigSync += Configs.Init;
}
protected override void TitleScene(bool isFirstBoot)
{
}
protected override void HotRestore()
{
Configs.Init();
}
protected override void WorldStart()
{
if (Configs.AutoSetWorldModifierPlayerEvents.Value)
{
SetWorldModifierPlayerEvents();
}
State.RegisterRPC();
}
protected override void Shutdown()
{
State.UnregisterRPC();
}
private static void SetWorldModifierPlayerEvents()
{
if (ZNet.instance.IsServer())
{
ZoneSystem.instance.SetGlobalKey((GlobalKeys)13);
Logging<Plugin>.Message((object)"World modifier enabled: PlayerEvents", 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);
Timing.Delay((MonoBehaviour)(object)bossStone, 12f, (Action)delegate
{
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>();
if (Configs.SacrificeTrophyForBossLoot.Value)
{
componentInParent.m_active = false;
}
Logging<Plugin>.Info((object)"Are you in range for activation keys and visuals?", 0);
if (Player.m_localPlayer.IsNear(componentInParent))
{
if (Configs.PerPlayerBossStones.Value)
{
Player.m_localPlayer.ActivateBossStone(componentInParent);
}
SetInvokingSacrifice(componentInParent);
}
}
}
}