using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.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.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace ForsakenPowersPlus
{
[BepInPlugin("pavel.forsakenpowersplus", "Forsaken Powers Plus", "1.1.1")]
public class ForsakenPowersPlusMod : BaseUnityPlugin
{
internal static class Cfg
{
internal static ConfigEntry<bool> Debug;
internal static ConfigEntry<KeyCode> CycleHotkey;
internal static ConfigEntry<KeyCode> ResetHotkey;
internal static ConfigEntry<bool> EnableResetRemoval;
internal static ConfigEntry<float> BuffCooldownSeconds;
internal static ConfigEntry<float> BuffDurationSeconds;
internal static ConfigEntry<bool> PassiveMode;
internal static ConfigEntry<bool> EnableAltAllAtOnce;
internal static ConfigEntry<bool> LockConfigOnServer;
internal static ConfigEntry<string> MsgPowerSelected;
internal static ConfigEntry<string> MsgPowerReset;
internal static ConfigEntry<string> MsgPowerReady;
internal static ConfigEntry<string> MsgPowerLocked;
internal static ConfigEntry<string> MsgNoUnlocked;
internal static void Bind(ConfigFile cfg)
{
Debug = cfg.Bind<bool>("1 - Mod", "Debug", false, "Enable debug logging.");
CycleHotkey = cfg.Bind<KeyCode>("2 - Hotkeys", "CyclePowerHotkey", (KeyCode)288, "Cycle through unlocked Forsaken Powers.");
ResetHotkey = cfg.Bind<KeyCode>("2 - Hotkeys", "ResetCooldownHotkey", (KeyCode)289, "Reset Forsaken Power cooldown and optionally remove active GP_ status effects.");
EnableResetRemoval = cfg.Bind<bool>("3 - Behavior", "ResetRemovesActivePowers", true, "If true, reset will remove active GP_ status effects. If false, only cooldown is reset.");
BuffCooldownSeconds = cfg.Bind<float>("4 - Buff Tuning", "GuardianBuffCooldownSeconds", 1200f, "Cooldown for guardian powers (seconds).");
BuffDurationSeconds = cfg.Bind<float>("4 - Buff Tuning", "GuardianBuffDurationSeconds", 300f, "Duration for guardian powers (seconds).");
PassiveMode = cfg.Bind<bool>("5 - Modes", "PassiveMode", false, "If true, GP_ effects become passive (ttl=0, cooldown=0).");
EnableAltAllAtOnce = cfg.Bind<bool>("5 - Modes", "EnableAltAllAtOnce", true, "If true, holding Alt while activating power will activate all unlocked powers at once.");
LockConfigOnServer = cfg.Bind<bool>("6 - Server", "LockConfigOnServer", true, "If true (recommended), server config becomes authoritative and is pushed to clients.");
MsgPowerSelected = cfg.Bind<string>("7 - Messages", "PowerSelected", "Power Selected", "Message shown when a power is selected.");
MsgPowerReset = cfg.Bind<string>("7 - Messages", "PowerReset", "Forsaken Power Has Been Reset", "Message shown when power is reset.");
MsgPowerReady = cfg.Bind<string>("7 - Messages", "PowerReady", "Ready To Stack Another Power", "Message shown when cooldown is reset but active powers are kept.");
MsgPowerLocked = cfg.Bind<string>("7 - Messages", "PowerLocked", "Forsaken power is locked (boss not defeated yet).", "Message shown when trying to use locked power.");
MsgNoUnlocked = cfg.Bind<string>("7 - Messages", "NoUnlocked", "No unlocked Forsaken powers yet.", "Message shown when cycling but nothing is unlocked.");
}
}
internal static class Effective
{
internal static bool Debug;
internal static KeyCode CycleHotkey;
internal static KeyCode ResetHotkey;
internal static bool EnableResetRemoval;
internal static float BuffCooldownSeconds;
internal static float BuffDurationSeconds;
internal static bool PassiveMode;
internal static bool EnableAltAllAtOnce;
internal static bool LockConfigOnServer;
internal static string MsgPowerSelected;
internal static string MsgPowerReset;
internal static string MsgPowerReady;
internal static string MsgPowerLocked;
internal static string MsgNoUnlocked;
internal static bool ReceivedFromServer;
internal static void LoadFromLocal()
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
Debug = Cfg.Debug.Value;
CycleHotkey = Cfg.CycleHotkey.Value;
ResetHotkey = Cfg.ResetHotkey.Value;
EnableResetRemoval = Cfg.EnableResetRemoval.Value;
BuffCooldownSeconds = Cfg.BuffCooldownSeconds.Value;
BuffDurationSeconds = Cfg.BuffDurationSeconds.Value;
PassiveMode = Cfg.PassiveMode.Value;
EnableAltAllAtOnce = Cfg.EnableAltAllAtOnce.Value;
LockConfigOnServer = Cfg.LockConfigOnServer.Value;
MsgPowerSelected = Cfg.MsgPowerSelected.Value;
MsgPowerReset = Cfg.MsgPowerReset.Value;
MsgPowerReady = Cfg.MsgPowerReady.Value;
MsgPowerLocked = Cfg.MsgPowerLocked.Value;
MsgNoUnlocked = Cfg.MsgNoUnlocked.Value;
ReceivedFromServer = false;
}
internal static void ApplyFromServer(bool debug, int cycleHotkey, int resetHotkey, bool enableResetRemoval, float buffCooldownSeconds, float buffDurationSeconds, bool passiveMode, bool enableAltAllAtOnce, bool lockConfigOnServer, string msgPowerSelected, string msgPowerReset, string msgPowerReady, string msgPowerLocked, string msgNoUnlocked)
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
Debug = debug;
CycleHotkey = (KeyCode)cycleHotkey;
ResetHotkey = (KeyCode)resetHotkey;
EnableResetRemoval = enableResetRemoval;
BuffCooldownSeconds = buffCooldownSeconds;
BuffDurationSeconds = buffDurationSeconds;
PassiveMode = passiveMode;
EnableAltAllAtOnce = enableAltAllAtOnce;
LockConfigOnServer = lockConfigOnServer;
MsgPowerSelected = msgPowerSelected ?? "Power Selected";
MsgPowerReset = msgPowerReset ?? "Forsaken Power Has Been Reset";
MsgPowerReady = msgPowerReady ?? "Ready To Stack Another Power";
MsgPowerLocked = msgPowerLocked ?? "Forsaken power is locked (boss not defeated yet).";
MsgNoUnlocked = msgNoUnlocked ?? "No unlocked Forsaken powers yet.";
ReceivedFromServer = true;
}
}
public const string ModGuid = "pavel.forsakenpowersplus";
public const string ModName = "Forsaken Powers Plus";
public const string ModVersion = "1.1.1";
internal static ManualLogSource Log;
private Harmony _harmony;
internal const string RpcConfigRequest = "FPP_Config_Request";
internal const string RpcConfigPush = "FPP_Config_Push";
internal static bool IsServer
{
get
{
if ((Object)(object)ZNet.instance != (Object)null)
{
return ZNet.instance.IsServer();
}
return false;
}
}
internal static bool IsClient
{
get
{
if ((Object)(object)ZNet.instance != (Object)null)
{
return !ZNet.instance.IsServer();
}
return false;
}
}
private void Awake()
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
Cfg.Bind(((BaseUnityPlugin)this).Config);
Effective.LoadFromLocal();
_harmony = new Harmony("pavel.forsakenpowersplus");
_harmony.PatchAll();
Log.LogInfo((object)"Forsaken Powers Plus v1.1.1 loaded");
}
private void OnDestroy()
{
try
{
Harmony harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
catch
{
}
}
internal static void Dbg(string s)
{
if (Effective.Debug)
{
ManualLogSource log = Log;
if (log != null)
{
log.LogInfo((object)s);
}
}
}
internal static void RegisterRpcs()
{
if (ZRoutedRpc.instance != null)
{
ZRoutedRpc.instance.Register<ZPackage>("FPP_Config_Request", (Action<long, ZPackage>)OnConfigRequest);
ZRoutedRpc.instance.Register<ZPackage>("FPP_Config_Push", (Action<long, ZPackage>)OnConfigPush);
}
}
private static void OnConfigRequest(long sender, ZPackage pkg)
{
if (IsServer)
{
PushConfigTo(sender);
}
}
private static void OnConfigPush(long sender, ZPackage pkg)
{
try
{
bool debug = pkg.ReadBool();
int cycleHotkey = pkg.ReadInt();
int resetHotkey = pkg.ReadInt();
bool enableResetRemoval = pkg.ReadBool();
float buffCooldownSeconds = pkg.ReadSingle();
float buffDurationSeconds = pkg.ReadSingle();
bool passiveMode = pkg.ReadBool();
bool enableAltAllAtOnce = pkg.ReadBool();
bool lockConfigOnServer = pkg.ReadBool();
string msgPowerSelected = pkg.ReadString();
string msgPowerReset = pkg.ReadString();
string msgPowerReady = pkg.ReadString();
string msgPowerLocked = pkg.ReadString();
string msgNoUnlocked = pkg.ReadString();
Effective.ApplyFromServer(debug, cycleHotkey, resetHotkey, enableResetRemoval, buffCooldownSeconds, buffDurationSeconds, passiveMode, enableAltAllAtOnce, lockConfigOnServer, msgPowerSelected, msgPowerReset, msgPowerReady, msgPowerLocked, msgNoUnlocked);
Dbg("[RPC] Config received from server");
BossTuning.ApplyPassiveModeAndTuning();
}
catch (Exception arg)
{
ManualLogSource log = Log;
if (log != null)
{
log.LogWarning((object)$"[RPC] Failed to parse config push: {arg}");
}
}
}
internal static void RequestConfigFromServer()
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Expected O, but got Unknown
if (IsClient && ZRoutedRpc.instance != null)
{
ZPackage val = new ZPackage();
ZRoutedRpc.instance.InvokeRoutedRPC(0L, "FPP_Config_Request", new object[1] { val });
Dbg("[RPC] Config request sent to server");
}
}
internal static void PushConfigTo(long targetPeerId)
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Expected O, but got Unknown
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Expected I4, but got Unknown
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Expected I4, but got Unknown
if (IsServer && ZRoutedRpc.instance != null)
{
Effective.LoadFromLocal();
ZPackage val = new ZPackage();
val.Write(Effective.Debug);
val.Write((int)Effective.CycleHotkey);
val.Write((int)Effective.ResetHotkey);
val.Write(Effective.EnableResetRemoval);
val.Write(Effective.BuffCooldownSeconds);
val.Write(Effective.BuffDurationSeconds);
val.Write(Effective.PassiveMode);
val.Write(Effective.EnableAltAllAtOnce);
val.Write(Effective.LockConfigOnServer);
val.Write(Effective.MsgPowerSelected ?? "");
val.Write(Effective.MsgPowerReset ?? "");
val.Write(Effective.MsgPowerReady ?? "");
val.Write(Effective.MsgPowerLocked ?? "");
val.Write(Effective.MsgNoUnlocked ?? "");
ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "FPP_Config_Push", new object[1] { val });
Dbg($"[RPC] Config pushed to {targetPeerId}");
}
}
}
[HarmonyPatch(typeof(ZNet), "Awake")]
public static class Patch_ZNet_Awake
{
private static void Postfix()
{
ForsakenPowersPlusMod.RegisterRpcs();
if (ForsakenPowersPlusMod.IsServer)
{
BossTuning.ApplyPassiveModeAndTuning();
}
}
}
[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
public static class Patch_ZNet_OnNewConnection
{
private static void Postfix(ZNet __instance, ZNetPeer peer)
{
if (!((Object)(object)__instance == (Object)null) && peer != null && __instance.IsServer())
{
ForsakenPowersPlusMod.PushConfigTo(peer.m_uid);
}
}
}
[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
public static class Patch_ZNet_RPC_PeerInfo
{
private static void Postfix(ZNet __instance)
{
if ((Object)(object)__instance != (Object)null && !__instance.IsServer())
{
ForsakenPowersPlusMod.RequestConfigFromServer();
}
}
}
internal static class BossTuning
{
internal static void ApplyPassiveModeAndTuning()
{
try
{
if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_StatusEffects == null)
{
return;
}
foreach (StatusEffect statusEffect in ObjectDB.instance.m_StatusEffects)
{
if ((Object)(object)statusEffect == (Object)null)
{
continue;
}
string name = ((Object)statusEffect).name;
if (!string.IsNullOrEmpty(name) && name.StartsWith("GP_"))
{
if (ForsakenPowersPlusMod.Effective.PassiveMode)
{
statusEffect.m_ttl = 0f;
statusEffect.m_cooldown = 0f;
}
else
{
statusEffect.m_ttl = ForsakenPowersPlusMod.Effective.BuffDurationSeconds;
statusEffect.m_cooldown = ForsakenPowersPlusMod.Effective.BuffCooldownSeconds;
}
}
}
ForsakenPowersPlusMod.Dbg("[Tuning] Applied passive mode and buff tuning");
}
catch (Exception arg)
{
ManualLogSource log = ForsakenPowersPlusMod.Log;
if (log != null)
{
log.LogWarning((object)$"[Tuning] Failed: {arg}");
}
}
}
}
[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
public static class Patch_ObjectDB_CopyOtherDB
{
private static void Postfix()
{
BossTuning.ApplyPassiveModeAndTuning();
}
}
[HarmonyPatch(typeof(Player), "Update")]
public static class Patch_Player_Update
{
private static void Prefix(Player __instance)
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)__instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || __instance != Player.m_localPlayer)
{
return;
}
if (Input.GetKeyDown(ForsakenPowersPlusMod.Effective.CycleHotkey))
{
string text = PowerLogic.FindNextAvailablePower(__instance);
if (string.IsNullOrEmpty(text))
{
((Character)__instance).Message((MessageType)1, ForsakenPowersPlusMod.Effective.MsgNoUnlocked, 0, (Sprite)null);
return;
}
__instance.SetGuardianPower(text);
((Character)__instance).Message((MessageType)1, ForsakenPowersPlusMod.Effective.MsgPowerSelected + ": " + text.Replace("GP_", ""), 0, (Sprite)null);
}
if (Input.GetKeyDown(ForsakenPowersPlusMod.Effective.ResetHotkey))
{
__instance.m_guardianPowerCooldown = 0.1f;
if (ForsakenPowersPlusMod.Effective.EnableResetRemoval)
{
PowerLogic.RemoveAllGuardianEffects(__instance);
((Character)__instance).Message((MessageType)1, ForsakenPowersPlusMod.Effective.MsgPowerReset, 0, (Sprite)null);
}
else
{
((Character)__instance).Message((MessageType)1, ForsakenPowersPlusMod.Effective.MsgPowerReady, 0, (Sprite)null);
}
}
}
}
[HarmonyPatch(typeof(Player), "StartGuardianPower")]
public static class Patch_Player_StartGuardianPower
{
private static bool _altAtStart;
private static bool Prefix(Player __instance)
{
if ((Object)(object)__instance == (Object)null)
{
return true;
}
if ((Object)(object)Player.m_localPlayer != (Object)null && __instance == Player.m_localPlayer)
{
_altAtStart = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307);
}
else
{
_altAtStart = false;
}
if (!PowerLogic.IsPowerUnlockedByProgress(__instance.GetGuardianPowerName()))
{
if ((Object)(object)Player.m_localPlayer != (Object)null && __instance == Player.m_localPlayer)
{
((Character)__instance).Message((MessageType)1, ForsakenPowersPlusMod.Effective.MsgPowerLocked, 0, (Sprite)null);
}
return false;
}
return true;
}
private static void Postfix(Player __instance)
{
if ((Object)(object)__instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || __instance != Player.m_localPlayer || !_altAtStart)
{
return;
}
_altAtStart = false;
if (ForsakenPowersPlusMod.Effective.EnableAltAllAtOnce)
{
if (!ForsakenPowersPlusMod.IsServer)
{
ForsakenPowersPlusMod.Dbg("[AltAll] Client attempted - ignored (server applies)");
}
else
{
PowerLogic.ActivateAllUnlockedPowers(__instance);
}
}
}
}
internal static class PowerLogic
{
private static readonly Dictionary<string, string> PowerToGlobalKey = new Dictionary<string, string>(StringComparer.Ordinal)
{
["GP_Eikthyr"] = "defeated_eikthyr",
["GP_TheElder"] = "defeated_gdking",
["GP_Bonemass"] = "defeated_bonemass",
["GP_Moder"] = "defeated_dragon",
["GP_Yagluth"] = "defeated_goblinking",
["GP_Queen"] = "defeated_queen",
["GP_Fader"] = "defeated_fader"
};
internal static bool IsPowerUnlockedByProgress(string gpName)
{
if (string.IsNullOrEmpty(gpName))
{
return false;
}
if (!PowerToGlobalKey.TryGetValue(gpName, out var value) || string.IsNullOrEmpty(value))
{
return true;
}
if ((Object)(object)ZoneSystem.instance == (Object)null)
{
return true;
}
return ZoneSystem.instance.GetGlobalKey(value);
}
private static List<string> GetUnlockedGuardianPowers()
{
List<string> list = new List<string>();
if (ObjectDB.instance?.m_StatusEffects == null)
{
return list;
}
if ((Object)(object)ZoneSystem.instance == (Object)null)
{
return list;
}
foreach (StatusEffect statusEffect in ObjectDB.instance.m_StatusEffects)
{
if (!((Object)(object)statusEffect == (Object)null))
{
string name = ((Object)statusEffect).name;
if (!string.IsNullOrEmpty(name) && name.StartsWith("GP_") && IsPowerUnlockedByProgress(name))
{
list.Add(name);
}
}
}
return list.OrderBy<string, string>((string k) => k, StringComparer.Ordinal).ToList();
}
internal static string FindNextAvailablePower(Player player)
{
try
{
List<string> unlockedGuardianPowers = GetUnlockedGuardianPowers();
if (unlockedGuardianPowers.Count == 0)
{
return null;
}
string guardianPowerName = player.GetGuardianPowerName();
if (string.IsNullOrEmpty(guardianPowerName))
{
return unlockedGuardianPowers[0];
}
int num = unlockedGuardianPowers.IndexOf(guardianPowerName);
if (num < 0)
{
return unlockedGuardianPowers[0];
}
int index = (num + 1) % unlockedGuardianPowers.Count;
return unlockedGuardianPowers[index];
}
catch (Exception arg)
{
ManualLogSource log = ForsakenPowersPlusMod.Log;
if (log != null)
{
log.LogWarning((object)$"[Cycle] Failed: {arg}");
}
return null;
}
}
internal static void RemoveAllGuardianEffects(Player player)
{
try
{
SEMan sEMan = ((Character)player).GetSEMan();
if (sEMan == null)
{
return;
}
List<int> list = new List<int>();
foreach (StatusEffect statusEffect in sEMan.GetStatusEffects())
{
if (!((Object)(object)statusEffect == (Object)null))
{
string name = ((Object)statusEffect).name;
if (!string.IsNullOrEmpty(name) && name.StartsWith("GP_"))
{
list.Add(StringExtensionMethods.GetStableHashCode(name));
}
}
}
foreach (int item in list)
{
sEMan.RemoveStatusEffect(item, true);
}
}
catch (Exception arg)
{
ManualLogSource log = ForsakenPowersPlusMod.Log;
if (log != null)
{
log.LogWarning((object)$"[Reset] Failed: {arg}");
}
}
}
internal static void ActivateAllUnlockedPowers(Player player)
{
try
{
string current = player.GetGuardianPowerName();
if (string.IsNullOrEmpty(current))
{
return;
}
SEMan sEMan = ((Character)player).GetSEMan();
if (sEMan == null)
{
return;
}
HashSet<string> activeNames = new HashSet<string>(from se in sEMan.GetStatusEffects()
where (Object)(object)se != (Object)null
select ((Object)se).name into n
where !string.IsNullOrEmpty(n) && n.StartsWith("GP_")
select n, StringComparer.Ordinal);
List<string> list = (from gp in GetUnlockedGuardianPowers()
where gp != current && !activeNames.Contains(gp)
select gp).ToList();
if (list.Count == 0)
{
return;
}
foreach (string item in list)
{
int stableHashCode = StringExtensionMethods.GetStableHashCode(item);
ObjectDB instance = ObjectDB.instance;
if ((Object)(object)((instance != null) ? instance.GetStatusEffect(stableHashCode) : null) == (Object)null)
{
ForsakenPowersPlusMod.Dbg("[AltAll] Missing StatusEffect: " + item);
}
else
{
sEMan.AddStatusEffect(stableHashCode, true, 0, 0f);
}
}
ForsakenPowersPlusMod.Dbg($"[AltAll] Activated {list.Count} additional unlocked powers");
}
catch (Exception arg)
{
ManualLogSource log = ForsakenPowersPlusMod.Log;
if (log != null)
{
log.LogWarning((object)$"[AltAll] Failed: {arg}");
}
}
}
}
}