Decompiled source of GTFuckingXP v2.8.0
EndskApi.dll
Decompiled 2 days agousing System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.Json; using System.Text.Json.Serialization; using Agents; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BoosterImplants; using CellMenu; using EndskApi.Api; using EndskApi.Enums.EnemyKill; using EndskApi.Enums.Menus; using EndskApi.Information.Configs; using EndskApi.Information.EnemyKill; using EndskApi.Information.Menus; using EndskApi.Information.WeaponSwitcher; using EndskApi.Manager; using EndskApi.Manager.Internal; using EndskApi.Patches.Checkpoint; using EndskApi.Patches.EndLevel; using EndskApi.Patches.Enemy; using EndskApi.Patches.Init; using EndskApi.Patches.StartLevel; using EndskApi.Scripts; using Enemies; using GTFO.API; using GameData; using Gear; using HarmonyLib; using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Microsoft.CodeAnalysis; using Player; using SNetwork; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("EndskApi")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+git33620c7-dirty-master.33620c7e9766136dea115db87926bb0ec5e6969c")] [assembly: AssemblyProduct("EndskApi")] [assembly: AssemblyTitle("EndskApi")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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; } } } namespace EndskApi { [BepInPlugin("Endskill.EndskApi", "EndskApi", "1.3.1")] public class BepInExLoader : BasePlugin { public const string MODNAME = "EndskApi"; public const string AUTHOR = "Endskill"; public const string GUID = "Endskill.EndskApi"; public const string VERSION = "1.3.1"; public static Harmony Harmony { get; private set; } public static ConfigFile ConfigLoader { get; private set; } public override void Load() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown LogManager.SetLogger(((BasePlugin)this).Log); LogManager._debugMessagesActive = ((BasePlugin)this).Config.Bind<bool>("Dev Settings", "DebugMessages", false, "This settings activates/deactivates debug messages in the console for this specific plugin.").Value; ConfigLoader = ((BasePlugin)this).Config; ClassInjector.RegisterTypeInIl2Cpp<BaseMenu>(); ClassInjector.RegisterTypeInIl2Cpp<MainMenu>(); Harmony = new Harmony("Endskill.EndskApi"); } } [GeneratedCode("VersionInfoGenerator", "2.1.3+git35c0c2a-master")] [CompilerGenerated] internal static class VersionInfo { public const string RootNamespace = "EndskApi"; public const string Version = "1.0.0"; public const string VersionPrerelease = null; public const string VersionMetadata = "git33620c7-dirty-master"; public const string SemVer = "1.0.0+git33620c7-dirty-master"; public const string GitRevShort = "33620c7-dirty"; public const string GitRevLong = "33620c7e9766136dea115db87926bb0ec5e6969c-dirty"; public const string GitBranch = "master"; public const string GitTag = null; public const int GitCommitsSinceTag = 0; public const bool GitIsDirty = true; } } namespace EndskApi.Util { public static class MtfoUtils { public static string CustomPath { get; private set; } public static string Version { get; private set; } public static bool PluginExists { get; private set; } static MtfoUtils() { CustomPath = string.Empty; Version = string.Empty; PluginExists = false; if (!((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.TryGetValue("com.dak.MTFO", out var info)) { return; } PluginExists = true; try { Assembly? assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly x) => !x.IsDynamic && x.Location == info.Location); if (assembly == null) { throw new Exception("couldn't locate the MTFO assembly"); } Type[] types = assembly.GetTypes(); Type type = types.FirstOrDefault((Type x) => x.Name == "ConfigManager"); Type? type2 = types.FirstOrDefault((Type x) => x.Name == "MTFO"); if (type2 == null) { throw new Exception("couldn't locate MTFO's EntryPoint"); } if (type == null) { throw new Exception("couldn't locate MTFO's ConfigManager"); } FieldInfo? field = type2.GetField("VERSION", BindingFlags.Static | BindingFlags.Public); FieldInfo field2 = type.GetField("CustomPath", BindingFlags.Static | BindingFlags.Public); if (field == null) { throw new Exception("couldn't locate MTFO's Version"); } if (field2 == null) { throw new Exception("couldn't locate MTFO's CustomPath"); } CustomPath = (string)field2.GetValue(null); Version = (string)field.GetValue(null); types.FirstOrDefault((Type x) => x.Name == "HotReloader"); } catch (Exception ex) { LogManager.Error(ex.ToString()); } } } } namespace EndskApi.Scripts { public class BaseMenu : MonoBehaviour { protected static string _currentGeomorph; protected static bool _guiInitialized = false; protected static GUIStyle _normalStyle; protected static GUIStyle _titleStyle; protected static readonly float _menuX = 30f; protected static readonly float _menuY = (float)(Screen.height / 2) - 100f; protected MenuStates _currentState; protected List<Tool> _tools; public string PageTitle { get; set; } protected static string PageTitlePostfix { get; set; } = ""; public BaseMenu(IntPtr intPtr) : base(intPtr) { _tools = new List<Tool>(); } protected virtual void Update() { if (_currentState != MenuStates.Deactivated) { DefaultCheatsInputCheck(); } } protected virtual void OnGUI() { if (_currentState == MenuStates.Active) { CreateUi(_tools, PageTitle); } } public virtual void SetState(MenuStates newState) { _currentState = newState; if (newState == MenuStates.Deactivated) { ((Behaviour)this).enabled = false; } else { ((Behaviour)this).enabled = true; } } protected void DefaultCheatsInputCheck() { foreach (Tool tool in _tools) { if (tool.CheckInput.CheckKeyDown()) { tool.UseTool(tool); } } } [HideFromIl2Cpp] public static void CreateUi(List<Tool> cheats, string pageTitle) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0037: 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_0049: Expected O, but got Unknown //IL_00e2: Unknown result type (might be due to invalid IL or missing references) if (!_guiInitialized) { _titleStyle = new GUIStyle(GUI.skin.GetStyle("label")) { fontSize = 22 }; _normalStyle = new GUIStyle(GUI.skin.GetStyle("label")) { fontSize = 20 }; _guiInitialized = true; } GUI.contentColor = Color.white; GUI.Label(new Rect(_menuX, _menuY - 25f, 400f, 30f), _currentGeomorph, _normalStyle); GUI.Label(new Rect(_menuX, _menuY, 400f, 30f), pageTitle + PageTitlePostfix, _titleStyle); float num = 25f; foreach (Tool cheat in cheats) { GUI.Label(new Rect(_menuX, _menuY + num, 400f, 30f), cheat.ToString(), _normalStyle); num += 22f; } } [HideFromIl2Cpp] protected void ToggleTool(Tool cheat) { cheat.Toggle(); } } internal class MainMenu : BaseMenu { private Dictionary<Tool, BaseMenu> _toolToMenuMap; private List<BaseMenu> _unknownMenus; private List<IExtendedTool> _middleMouseClickTools = new List<IExtendedTool>(); private List<IExtendedTool> _timedTools = new List<IExtendedTool>(); private List<Tool> _hiddenTools = new List<Tool>(); private float _lastToolActive = Time.time; private (uint enemyId, string enemyName)[] _enemyNamesIdMap; private int _currentEnemyIndex; private int _keypadCounter; [HideFromIl2Cpp] internal static MainMenu Instance { get; private set; } public int CurrentEnemyIndex { get { return _currentEnemyIndex; } set { if (value >= _enemyNamesIdMap.Count() || value < 0) { value = _currentEnemyIndex; } _currentEnemyIndex = value; BaseMenu.PageTitlePostfix = ": " + _enemyNamesIdMap[value].enemyName; } } public MainMenu(IntPtr intPtr) : base(intPtr) { base.PageTitle = "MainMenu"; _toolToMenuMap = new Dictionary<Tool, BaseMenu>(); _unknownMenus = new List<BaseMenu>(); _currentState = MenuStates.HiddenAndActive; Instance = this; } public void Awake() { Il2CppArrayBase<EnemyDataBlock> allBlocks = GameDataBlockBase<EnemyDataBlock>.GetAllBlocks(); _enemyNamesIdMap = new(uint, string)[allBlocks.Count]; for (int i = 0; i < allBlocks.Count; i++) { _enemyNamesIdMap[i] = (enemyId: ((GameDataBlockBase<EnemyDataBlock>)(object)allBlocks[i]).persistentID, enemyName: ((GameDataBlockBase<EnemyDataBlock>)(object)allBlocks[i]).name); } CurrentEnemyIndex = 0; } [HideFromIl2Cpp] public void ActivateUnknownMenu<TScript>(TScript menu) where TScript : BaseMenu { MenuToggleUpdated(null, newToggle: false); _currentState = MenuStates.HiddenAndActive; _unknownMenus.Add(menu); } [HideFromIl2Cpp] public void AddPage(string ToolName, BaseMenu menu) { AddPage(new Tool(ToolName, GetFreeKeypadInput(), isToggle: false, base.ToggleTool, MenuToggleUpdated), menu); } [HideFromIl2Cpp] public void AddPage(Tool Tool, BaseMenu menu) { _toolToMenuMap.Add(Tool, menu); _tools.Add(Tool); _currentState = MenuStates.Active; } [HideFromIl2Cpp] public void AddMiddleMouseClickTool(IExtendedTool Tool) { _middleMouseClickTools.Add(Tool); } [HideFromIl2Cpp] public void RemoveMiddleMouseClickTool(IExtendedTool Tool) { _middleMouseClickTools.Remove(Tool); } [HideFromIl2Cpp] public void AddTimedTool(IExtendedTool tool) { _timedTools.Add(tool); } [HideFromIl2Cpp] public void RemoveTimedTool(IExtendedTool tool) { _timedTools.Remove(tool); } [HideFromIl2Cpp] public void AddHiddenTool(Tool tool) { _hiddenTools.Add(tool); } [HideFromIl2Cpp] public void RemoveHiddenTool(Tool tool) { _hiddenTools.Remove(tool); } protected override void Update() { if (Input.GetKeyDown((KeyCode)277)) { if (_currentState == MenuStates.Active || _currentState == MenuStates.HiddenAndActive) { _currentState = MenuStates.Deactivated; } else { _currentState = MenuStates.Active; } foreach (KeyValuePair<Tool, BaseMenu> item in _toolToMenuMap) { item.Key.CurrentToggleState = false; item.Value.SetState(MenuStates.Deactivated); } } if (_currentState != MenuStates.Deactivated) { foreach (Tool hiddenTool in _hiddenTools) { if (hiddenTool.CheckInput.CheckKeyDown()) { hiddenTool.UseTool(hiddenTool); } } } base.Update(); MainMenuSpecificUpdate(); } private void MainMenuSpecificUpdate() { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Invalid comparison between Unknown and I4 if (Input.GetKeyDown((KeyCode)275)) { CurrentEnemyIndex++; } if (Input.GetKeyDown((KeyCode)276)) { CurrentEnemyIndex--; } if (Input.GetMouseButtonDown(2)) { foreach (IExtendedTool middleMouseClickTool in _middleMouseClickTools) { middleMouseClickTool.ExtraToolAction(GetInformationPackage(middleMouseClickTool.InformationId)); } } if (!(_lastToolActive < Time.time)) { return; } foreach (IExtendedTool timedTool in _timedTools) { timedTool.ExtraToolAction(GetInformationPackage(timedTool.InformationId)); } if ((int)GameStateManager.CurrentStateName == 10) { BaseMenu._currentGeomorph = ((Object)((Agent)PlayerManager.GetLocalPlayerAgent()).CourseNode.m_area.m_geomorph).name; } else { BaseMenu._currentGeomorph = ""; } _lastToolActive = Time.time + 0.25f; } [HideFromIl2Cpp] private object GetInformationPackage(InformationId id) { return id switch { InformationId.None => null, InformationId.EnemyId => _enemyNamesIdMap[CurrentEnemyIndex], _ => null, }; } [HideFromIl2Cpp] private void MenuToggleUpdated(Tool Tool, bool newToggle) { foreach (BaseMenu unknownMenu in _unknownMenus) { unknownMenu.SetState(MenuStates.Deactivated); } _unknownMenus.Clear(); foreach (KeyValuePair<Tool, BaseMenu> item in _toolToMenuMap) { if (item.Key.Equals(Tool)) { item.Value.SetState((!newToggle) ? MenuStates.Deactivated : MenuStates.Active); item.Key.CurrentToggleState = newToggle; } else { item.Key.CurrentToggleState = false; item.Value.SetState(MenuStates.Deactivated); } } _currentState = (_toolToMenuMap.Any<KeyValuePair<Tool, BaseMenu>>((KeyValuePair<Tool, BaseMenu> it) => it.Key.CurrentToggleState) ? MenuStates.HiddenAndActive : MenuStates.Active); } [HideFromIl2Cpp] private InputTool GetFreeKeypadInput() { _keypadCounter++; switch (_keypadCounter) { case 1: return MenuInputProvider.KeyPad1; case 2: return MenuInputProvider.KeyPad2; case 3: return MenuInputProvider.KeyPad3; case 4: return MenuInputProvider.KeyPad4; case 5: return MenuInputProvider.KeyPad5; case 6: return MenuInputProvider.KeyPad6; case 7: return MenuInputProvider.KeyPad7; case 8: return MenuInputProvider.KeyPad8; case 9: return MenuInputProvider.KeyPad9; default: LogManager.Error("There are no free Keypad keys, for the added Menu page!\nPlease ask @Endskill#4992 to finally do his Page system."); throw new IndexOutOfRangeException("There are no free keypad keys to add this specific page."); } } } } namespace EndskApi.Patches.StartLevel { [HarmonyPatch(typeof(GS_InLevel))] internal class GsInLevelPatches { [HarmonyPatch("Enter")] [HarmonyPostfix] public static void EnterLevelPostfix() { LevelApi.InvokeStartLevelCallbacks(); } } } namespace EndskApi.Patches.Init { [HarmonyPatch(typeof(GuiManager))] internal class GUIManagerPatches { [HarmonyPatch("Setup")] [HarmonyPrefix] [HarmonyWrapSafe] public static void SetupPrefix() { InitApi.InvokeInitCallbacks(); } } [HarmonyPatch(typeof(CM_PageRundown_New))] internal class PageRundownNewPatches { [HarmonyPatch("PlaceRundown")] [HarmonyPostfix] [HarmonyWrapSafe] public static void PlaceRundownPostFix() { InitApi.InvokeInitCallbacks(); } } } namespace EndskApi.Patches.Enemy { [HarmonyBefore(new string[] { "Endskill.EndskApi", "com.dak.DamageNumbers" })] [HarmonyPatch(typeof(Dam_EnemyDamageBase))] internal class EnemyDamageBasePatches { [HarmonyPatch("ReceiveMeleeDamage")] [HarmonyPrefix] public static void MeleePrefix(Dam_EnemyDamageBase __instance, ref pFullDamageData data) { EnemyDamageBasePatchApi.InvokeReceiveMeleePrefix(__instance, ref data); } [HarmonyPatch("ReceiveMeleeDamage")] [HarmonyPostfix] public static void MeleePostfix(Dam_EnemyDamageBase __instance, ref pFullDamageData data) { EnemyDamageBasePatchApi.InvokeReceiveMeleePostfix(__instance, ref data); } [HarmonyPatch("ReceiveBulletDamage")] [HarmonyPrefix] public static void BulletPrefix(Dam_EnemyDamageBase __instance, ref pBulletDamageData data) { EnemyDamageBasePatchApi.InvokeReceiveBulletPrefix(__instance, ref data); } [HarmonyPatch("ReceiveBulletDamage")] [HarmonyPostfix] public static void BulletPostfix(Dam_EnemyDamageBase __instance, ref pBulletDamageData data) { EnemyDamageBasePatchApi.InvokeReceiveBulletPostfix(__instance, ref data); } [HarmonyPatch("ReceiveExplosionDamage")] [HarmonyPrefix] public static void ExplosionPrefix(Dam_EnemyDamageBase __instance, ref pExplosionDamageData data) { EnemyDamageBasePatchApi.InvokeReceiveExplosionPrefix(__instance, ref data); } [HarmonyPatch("ReceiveExplosionDamage")] [HarmonyPostfix] public static void ExplosionPostfix(Dam_EnemyDamageBase __instance, ref pExplosionDamageData data) { EnemyDamageBasePatchApi.InvokeReceiveExplosionPostfix(__instance, ref data); } } [HarmonyPatch] internal static class EnemyTagPatches { private static bool _inBotTag; [HarmonyPatch(typeof(EnemyScanner), "BotTag")] [HarmonyPrefix] private static void Pre_BotTag() { _inBotTag = true; } [HarmonyPatch(typeof(EnemyScanner), "BotTag")] [HarmonyPostfix] private static void Post_BotTag() { _inBotTag = false; } [HarmonyPatch(typeof(ToolSyncManager), "WantToTagEnemy")] [HarmonyPostfix] private static void Post_TagEnemy(EnemyAgent enemy) { if (!_inBotTag) { NetworkManager.SendBiotag(enemy); } } } [HarmonyPatch(typeof(MineDeployerInstance_Detonate_Explosive))] internal static class MineDeployerExplosivePatches { public static PlayerAgent? CachedAgent { get; private set; } [HarmonyPatch("DoExplode")] [HarmonyPrefix] public static void ExplodePrefix(MineDeployerInstance_Detonate_Explosive __instance) { CachedAgent = __instance.m_core.Owner; } [HarmonyPatch("DoExplode")] [HarmonyPostfix] public static void ExplodePostfix() { CachedAgent = null; } } } namespace EndskApi.Patches.EndLevel { [HarmonyPatch(typeof(GS_AfterLevel))] public class GsAfterLevelPatches { [HarmonyPatch("CleanupAfterExpedition")] [HarmonyPrefix] public static void CleanupPrefix() { LevelApi.InvokeEndLevelCallbacks(); } } } namespace EndskApi.Patches.Checkpoint { [HarmonyPatch(typeof(CheckpointManager))] internal static class CheckpointManagerPatches { private static Vector3 _lastCheckpointPos = Vector3.zero; [HarmonyPatch("OnStateChange")] [HarmonyPostfix] public static void OnCheckpointStateChange(pCheckpointState newState) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //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) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if ((int)newState.lastInteraction == 1 && _lastCheckpointPos != newState.doorLockPosition) { _lastCheckpointPos = newState.doorLockPosition; CheckpointApi.InvokeCheckpointReachedCallbacks(); } else if ((int)newState.lastInteraction == 2) { CheckpointApi.InvokeCheckpointReloadedCallbacks(); } } [HarmonyPatch("OnLevelCleanup")] [HarmonyPostfix] public static void OnLevelCleanupPostfix() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) _lastCheckpointPos = Vector3.zero; CheckpointApi.InvokeCheckpointCleanupCallbacks(); NetworkManager.SendCheckpointCleanups(); } } } namespace EndskApi.Manager { public static class LogManager { private static ManualLogSource logger; internal static bool _debugMessagesActive; internal static void SetLogger(ManualLogSource log) { logger = log; } public static void Verbose(object msg) { if (_debugMessagesActive) { logger.LogInfo(msg); } } public static void Debug(object msg) { if (_debugMessagesActive) { logger.LogDebug(msg); } } public static void Message(object msg) { if (_debugMessagesActive) { logger.LogMessage(msg); } } public static void Error(object msg) { logger.LogError(msg); } public static void Warn(object msg) { logger.LogWarning(msg); } } public static class MenuInputProvider { private static MenuRebinding _bindings; public static InputTool F1 => new InputTool(_bindings.F1, $": [{_bindings.F1}]"); public static InputTool F2 => new InputTool(_bindings.F2, $": [{_bindings.F2}]"); public static InputTool F3 => new InputTool(_bindings.F3, $": [{_bindings.F3}]"); public static InputTool F4 => new InputTool(_bindings.F4, $": [{_bindings.F4}]"); public static InputTool F5 => new InputTool(_bindings.F5, $": [{_bindings.F5}]"); public static InputTool F6 => new InputTool(_bindings.F6, $": [{_bindings.F6}]"); public static InputTool F7 => new InputTool(_bindings.F7, $": [{_bindings.F7}]"); public static InputTool F8 => new InputTool(_bindings.F8, $": [{_bindings.F8}]"); public static InputTool F9 => new InputTool(_bindings.F9, $": [{_bindings.F9}]"); public static InputTool F10 => new InputTool(_bindings.F10, $": [{_bindings.F10}]"); public static InputTool KeyPad1 => new InputTool(_bindings.Keypad1, $": [{_bindings.Keypad1}]"); public static InputTool KeyPad2 => new InputTool(_bindings.Keypad2, $": [{_bindings.Keypad2}]"); public static InputTool KeyPad3 => new InputTool(_bindings.Keypad3, $": [{_bindings.Keypad3}]"); public static InputTool KeyPad4 => new InputTool(_bindings.Keypad4, $": [{_bindings.Keypad4}]"); public static InputTool KeyPad5 => new InputTool(_bindings.Keypad5, $": [{_bindings.Keypad5}]"); public static InputTool KeyPad6 => new InputTool(_bindings.Keypad6, $": [{_bindings.Keypad6}]"); public static InputTool KeyPad7 => new InputTool(_bindings.Keypad7, $": [{_bindings.Keypad7}]"); public static InputTool KeyPad8 => new InputTool(_bindings.Keypad8, $": [{_bindings.Keypad8}]"); public static InputTool KeyPad9 => new InputTool(_bindings.Keypad9, $": [{_bindings.Keypad9}]"); static MenuInputProvider() { JsonSerializerOptions options = new JsonSerializerOptions { Converters = { (JsonConverter)new JsonStringEnumConverter() } }; _bindings = JsonSerializer.Deserialize<MenuRebinding>(BepInExLoader.ConfigLoader.Bind<string>("MenuApi", "KeyBindings", "{\"F1\":\"F1\",\"F2\":\"F2\",\"F3\":\"F3\",\"F4\":\"F4\",\"F5\":\"F5\",\"F6\":\"F6\",\"F7\":\"F7\",\"F8\":\"F8\",\"F9\":\"F9\",\"F10\":\"None\",\"Keypad1\":\"Keypad1\",\"Keypad2\":\"Keypad2\",\"Keypad3\":\"Keypad3\",\"Keypad4\":\"Keypad4\",\"Keypad5\":\"Keypad5\",\"Keypad6\":\"Keypad6\",\"Keypad7\":\"Keypad7\",\"Keypad8\":\"Keypad8\",\"Keypad9\":\"Keypad9\"}", "You can remap keys here.").Value, options); } } } namespace EndskApi.Manager.Internal { public class InformationCache { private readonly Dictionary<string, Dictionary<object, object>> _cache; public static InformationCache Instance { get; } = new InformationCache(); public InformationCache() { _cache = new Dictionary<string, Dictionary<object, object>>(); } public void SaveInformation(object key, object info, string mod) { GetCacheOfMod(mod)[key] = info; } public T GetInformation<T>(object key, string mod) { if (GetCacheOfMod(mod).TryGetValue(key, out object value)) { if (value is T) { return (T)value; } throw new KeyNotFoundException($"Tried to get information with key {key}, but it is type {value.GetType().Name}, not type {typeof(T).Name}"); } LogManager.Error($"There was no information with the key {key}"); throw new KeyNotFoundException($"There was no information with the key {key}!"); } public bool TryGetInformation<T>(object key, out T info, string mod, bool logNotFound = true) { if (GetCacheOfMod(mod).TryGetValue(key, out object value)) { if (value is T val) { info = val; return true; } LogManager.Warn($"Tried to get information with key {key}, but it is type {value.GetType().Name}, not type {typeof(T).Name}"); info = default(T); return false; } if (logNotFound) { LogManager.Warn($"There was no informaiton with the key {key}"); } info = default(T); return false; } public void RemoveInformation(object key, string mod) { if (TryGetCacheOfMod(mod, out Dictionary<object, object> cache)) { cache.Remove(key); } } public bool ContainsKey(object key, string mod) { if (TryGetCacheOfMod(mod, out Dictionary<object, object> cache)) { return cache.ContainsKey(key); } return false; } private Dictionary<object, object> GetCacheOfMod(string mod) { if (!_cache.TryGetValue(mod, out Dictionary<object, object> value)) { value = new Dictionary<object, object>(); _cache[mod] = value; } return value; } private bool TryGetCacheOfMod(string mod, out Dictionary<object, object> cache) { return _cache.TryGetValue(mod, out cache); } } internal static class NetworkManager { [StructLayout(LayoutKind.Sequential, Size = 1)] internal struct DummyStruct { } [StructLayout(LayoutKind.Sequential, Size = 1)] internal struct EquipGearStruct { } private const string _sendCheckpointReachedKey = "CheckpointHasBeenReached"; private const string _sendCheckpointCleanupKey = "CheckpointCleanup"; private const string _sendBiotagKey = "BiotagApplied"; public static void Setup() { NetworkAPI.RegisterEvent<DummyStruct>("CheckpointCleanup", (Action<ulong, DummyStruct>)ReceiveCheckpointCleanup); NetworkAPI.RegisterEvent<DummyStruct>("CheckpointHasBeenReached", (Action<ulong, DummyStruct>)ReceiveCheckpointReached); NetworkAPI.RegisterEvent<pEnemyAgent>("BiotagApplied", (Action<ulong, pEnemyAgent>)ReceiveBiotag); } private static void ReceiveCheckpointReached(ulong snetPlayer, DummyStruct _) { CheckpointApi.InvokeCheckpointReachedCallbacks(); } private static void ReceiveCheckpointCleanup(ulong snetPlayer, DummyStruct _) { CheckpointApi.InvokeCheckpointCleanupCallbacks(); } private static void ReceiveBiotag(ulong lookup, pEnemyAgent packet) { EnemyAgent val = default(EnemyAgent); SNet_Player val2 = default(SNet_Player); if (((pEnemyAgent)(ref packet)).TryGet(ref val) && ((Agent)val).Alive && SNet.TryGetPlayer(lookup, ref val2)) { EnemyKillApi.RegisterBiotag(val, ((Il2CppObjectBase)val2.PlayerAgent).Cast<PlayerAgent>()); } } public static void SendCheckpointReached() { NetworkAPI.InvokeEvent<DummyStruct>("CheckpointHasBeenReached", default(DummyStruct), (SNet_ChannelType)2); } public static void SendCheckpointCleanups() { NetworkAPI.InvokeEvent<DummyStruct>("CheckpointCleanup", default(DummyStruct), (SNet_ChannelType)2); } public static void SendBiotag(EnemyAgent enemy) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) pEnemyAgent val = default(pEnemyAgent); ((pEnemyAgent)(ref val)).Set(enemy); NetworkAPI.InvokeEvent<pEnemyAgent>("BiotagApplied", val, SNet.Master, (SNet_ChannelType)2); } public static void SendEquipGear(GearInfo info) { } } } namespace EndskApi.Information.WeaponSwitcher { public class GearInfo { public InventorySlotAmmo InventorySlotAmmo { get; set; } public InventorySlot Slot { get; set; } public AmmoType AmmoType { get; set; } public GearIDRange GearId { get; set; } public int AmmunitionInMagazine { get; set; } public GearInfo(GearIDRange gearId, int ammunitionInMagazine, InventorySlotAmmo inventorySlotAmmo) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) GearId = gearId; AmmunitionInMagazine = ammunitionInMagazine; InventorySlotAmmo = inventorySlotAmmo; AmmoType = inventorySlotAmmo.AmmoType; Slot = inventorySlotAmmo.Slot; } } } namespace EndskApi.Information.Menus { public interface IExtendedTool { Action<object> ExtraToolAction { get; set; } InformationId InformationId { get; set; } } public class InputTool { public KeyCode InputKey { get; set; } public string Postfix { get; set; } public InputTool(KeyCode inputKey, string postfix) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) InputKey = inputKey; Postfix = postfix; } public virtual bool CheckKeyDown() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Input.GetKeyDown(InputKey); } public virtual bool CheckKey() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Input.GetKey(InputKey); } } public class Tool { public string Text { get; set; } public string TextPostfix { get; set; } public InputTool CheckInput { get; } public Action<Tool> UseTool { get; set; } public Action<Tool, bool> OnToggleUpdate { get; set; } public bool IsToggle { get; set; } public bool CurrentToggleState { get; set; } public object Extra { get; set; } public Tool() { } public Tool(string text, InputTool input, Action<Tool, bool> onToggleUpdate, bool startState) { Text = text; CheckInput = input; IsToggle = true; UseTool = ToggleTool; OnToggleUpdate = onToggleUpdate; CurrentToggleState = startState; } public Tool(string text, InputTool input, bool isToggle, Action<Tool> useCheat, Action<Tool, bool> onToggleUpdate = null, bool currentToggleState = false) { Text = text; CheckInput = input; IsToggle = isToggle; UseTool = useCheat; OnToggleUpdate = onToggleUpdate; CurrentToggleState = currentToggleState; } public void Toggle() { CurrentToggleState = !CurrentToggleState; OnToggleUpdate?.Invoke(this, CurrentToggleState); } public override string ToString() { if (IsToggle) { return $"{Text}{TextPostfix}{CheckInput.Postfix} {(CurrentToggleState ? "ON" : "OFF")}"; } return Text + TextPostfix + CheckInput.Postfix; } private static void ToggleTool(Tool tool) { tool.Toggle(); } } public class ToolExtended : Tool, IExtendedTool { public Action<object> ExtraToolAction { get; set; } public InformationId InformationId { get; set; } public ToolExtended(string text, InputTool input, bool isToggle, Action<Tool> useCheat, Action<object> extraCheatAction, Action<Tool, bool> onToggleUpdate = null, bool currentToggleState = false, InformationId infoId = InformationId.None) : base(text, input, isToggle, useCheat, onToggleUpdate, currentToggleState) { ExtraToolAction = extraCheatAction; InformationId = infoId; } public override string ToString() { return base.ToString(); } } } namespace EndskApi.Information.EnemyKill { public class EnemyKillDistribution { public EnemyAgent KilledEnemyAgent { get; set; } public Dictionary<int, float> DamageDistributions { get; set; } public HashSet<int> TaggedBy { get; set; } public PlayerAgent? LastHitDealtBy { get; set; } public LastHitType lastHitType { get; set; } public EnemyKillDistribution(EnemyAgent agentToKill) { KilledEnemyAgent = agentToKill; DamageDistributions = new Dictionary<int, float>(); TaggedBy = new HashSet<int>(); } public void AddPlayerTagged(PlayerAgent agent) { TaggedBy.Add(agent.PlayerSlotIndex); } public void AddDamageDealtByPlayerAgent(PlayerAgent agent, float damage) { if (!DamageDistributions.TryGetValue(agent.PlayerSlotIndex, out var value)) { DamageDistributions.Add(agent.PlayerSlotIndex, damage); } else { DamageDistributions[agent.PlayerSlotIndex] = value + damage; } } public bool DidSnetBiotag(SNet_Player player) { return TaggedBy.Contains(player.PlayerSlotIndex()); } public float GetDamageDealtBySnet(SNet_Player player) { int key = player.PlayerSlotIndex(); if (DamageDistributions.TryGetValue(key, out var value)) { return value; } return 0f; } } public class DamageDistribution { public int PlayerSlotIndex { get; set; } public float DamageDealt { get; set; } public DamageDistribution(int slotIndex, float damage) { PlayerSlotIndex = slotIndex; DamageDealt = damage; } } } namespace EndskApi.Information.Configs { public class MenuRebinding { public KeyCode F1 { get; set; } public KeyCode F2 { get; set; } public KeyCode F3 { get; set; } public KeyCode F4 { get; set; } public KeyCode F5 { get; set; } public KeyCode F6 { get; set; } public KeyCode F7 { get; set; } public KeyCode F8 { get; set; } public KeyCode F9 { get; set; } public KeyCode F10 { get; set; } public KeyCode Keypad1 { get; set; } public KeyCode Keypad2 { get; set; } public KeyCode Keypad3 { get; set; } public KeyCode Keypad4 { get; set; } public KeyCode Keypad5 { get; set; } public KeyCode Keypad6 { get; set; } public KeyCode Keypad7 { get; set; } public KeyCode Keypad8 { get; set; } public KeyCode Keypad9 { get; set; } public MenuRebinding() { F1 = (KeyCode)282; F2 = (KeyCode)283; F3 = (KeyCode)284; F4 = (KeyCode)285; F5 = (KeyCode)286; F6 = (KeyCode)287; F7 = (KeyCode)288; F8 = (KeyCode)289; F9 = (KeyCode)290; Keypad1 = (KeyCode)257; Keypad2 = (KeyCode)258; Keypad3 = (KeyCode)259; Keypad4 = (KeyCode)260; Keypad5 = (KeyCode)261; Keypad6 = (KeyCode)262; Keypad7 = (KeyCode)263; Keypad8 = (KeyCode)264; Keypad9 = (KeyCode)265; } } } namespace EndskApi.Information.BoosterInfo { public class BoosterCondition { public string Name { get; set; } public string ShortName { get; set; } public BoosterCondition Condition { get; set; } } public class BoosterEffect { public string Name { get; set; } public string ShortName { get; set; } public AgentModifier Effect { get; set; } public float Value { get; set; } public BoosterEffect(string name, string shortName, AgentModifier effect, float value) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) Name = name; ShortName = shortName; Effect = effect; Value = value; } } public interface IBooster { object Category { get; set; } List<BoosterEffect> BoosterEffects { get; set; } List<BoosterCondition> BoosterConditions { get; set; } } } namespace EndskApi.Enums.Menus { public enum InformationId { None, EnemyId } public enum MenuStates { Active, HiddenAndActive, Deactivated } } namespace EndskApi.Enums.EnemyKill { public enum LastHitType { Melee, ShootyWeapon, Explosion } } namespace EndskApi.Enums.Booster { public enum CustomBoosterCategory { Muted, Bold, Aggressive, NotAccessible } public enum CustomBoosterModifiers { None = 0, RegenerationCap = 1, RegenerationSpeed = 2, HealSupport = 3, ReviveSpeedSupport = 4, ReviveStartHealthSupport = 5, MeleeResistance = 6, ProjectileResistance = 7, InfectionResistance = 8, PistolDamage = 50, SMGDamage = 51, DMRDamage = 52, AssaultRifleDamage = 53, CarbineDamage = 54, AutoPistolDamage = 55, HELDamage = 56, ShotgunDamage = 58, RevolverDamage = 59, SniperDamage = 60, BurstCannonDamage = 61, MachineGunDamage = 62, MachinePistolDamage = 63, RifleDamage = 64, BurstRifleDamage = 65, DoubleTapRifle = 66, BullpupRifleDamage = 67, CombatShotgunDamage = 68, ChokeModShotgunDamage = 69, StandardWeaponDamage = 70, SpecialWeaponDamage = 71, GlueStrength = 100, GlueEfficiency = 101, SentryGunSpeed = 102, SentryGunDamage = 103, SentryGunLongRangeDamage = 104, SentryGunShortRangeDamage = 105, TripMineDamage = 106, ScannerRechargeSpeed = 107, AmmoSupport = 108, HackingProficiency = 150, ComputerProcessingSpeed = 151, InitialAmmoStandard = 152, InitialAmmoSpecial = 153, InitialAmmoTool = 154, FogRepellerEffect = 155, GlowstickEffect = 156, BioscanSpeed = 157, MeleeDamage = 200 } } namespace EndskApi.Api { public static class CacheApi { private const string GlobalCache = "GlobalCache"; internal const string InternalCache = "EndskApi"; private static readonly InformationCache _informationCache; static CacheApi() { _informationCache = InformationCache.Instance; } public static void SaveInformation(object key, object info, string mod = "GlobalCache") { _informationCache.SaveInformation(key, info, mod); } public static void SaveInstance<T>(T info, string mod = "GlobalCache") { SaveInformation(typeof(T), info, mod); } public static T GetInformation<T>(object key, string mod = "GlobalCache") { return _informationCache.GetInformation<T>(key, mod); } public static T GetInstance<T>(string mod = "GlobalCache") { return GetInformation<T>(typeof(T), mod); } public static bool TryGetInformation<T>(object key, out T information, string mod = "GlobalCache", bool logNotFound = true) { return _informationCache.TryGetInformation<T>(key, out information, mod, logNotFound); } public static bool TryGetInstance<T>(out T information, string mod = "GlobalCache", bool logNotFound = true) { return _informationCache.TryGetInformation<T>(typeof(T), out information, mod, logNotFound); } public static void RemoveInformation(object key, string mod = "GlobalCache") { _informationCache.RemoveInformation(key, mod); } public static void RemoveInstance<T>(string mod = "GlobalCache") { RemoveInformation(typeof(T), mod); } public static bool ContainsKey(object key, string mod = "GlobalCache") { return _informationCache.ContainsKey(key, mod); } public static InformationCache GetInformationCache() { return _informationCache; } } public static class CheckpointApi { private const string CheckpointReachedKey = "CheckpointReachedKey"; private const string CheckpointReloadedKey = "CheckpointReloadedKey"; private const string CleanupCheckpointKey = "CheckpointCleanupKey"; static CheckpointApi() { BepInExLoader.Harmony.PatchAll(typeof(CheckpointManagerPatches)); NetworkManager.Setup(); } public static void AddCheckpointReachedCallback(Action callBack) { if (!CacheApi.TryGetInformation<List<Action>>("CheckpointReachedKey", out var information, "EndskApi", logNotFound: false)) { information = new List<Action>(); CacheApi.SaveInformation("CheckpointReachedKey", information, "EndskApi"); } information.Add(callBack); } public static void AddCheckpointReloadedCallback(Action callBack) { if (!CacheApi.TryGetInformation<List<Action>>("CheckpointReloadedKey", out var information, "EndskApi", logNotFound: false)) { information = new List<Action>(); CacheApi.SaveInformation("CheckpointReloadedKey", information, "EndskApi"); } information.Add(callBack); } public static void AddCheckpointCleanupCallback(Action callBack) { if (!CacheApi.TryGetInformation<List<Action>>("CheckpointCleanupKey", out var information, "EndskApi", logNotFound: false)) { information = new List<Action>(); CacheApi.SaveInformation("CheckpointCleanupKey", information, "EndskApi"); } information.Add(callBack); } public static void RemoveCheckpointReachedCallback(Action callBack) { if (CacheApi.TryGetInformation<List<Action>>("CheckpointReachedKey", out var information, "EndskApi", logNotFound: false)) { information.Remove(callBack); } } public static void RemoveCheckpointReloadedCallback(Action callBack) { if (CacheApi.TryGetInformation<List<Action>>("CheckpointReloadedKey", out var information, "EndskApi", logNotFound: false)) { information.Remove(callBack); } } public static void RemoveCheckpointCleanupCallback(Action callBack) { if (CacheApi.TryGetInformation<List<Action>>("CheckpointCleanupKey", out var information, "EndskApi", logNotFound: false)) { information.Remove(callBack); } } internal static void InvokeCheckpointReachedCallbacks() { if (!CacheApi.TryGetInformation<List<Action>>("CheckpointReachedKey", out var information, "EndskApi", logNotFound: false)) { return; } foreach (Action item in information) { item(); } } internal static void InvokeCheckpointReloadedCallbacks() { if (!CacheApi.TryGetInformation<List<Action>>("CheckpointReloadedKey", out var information, "EndskApi", logNotFound: false)) { return; } foreach (Action item in information) { item(); } } internal static void InvokeCheckpointCleanupCallbacks() { if (!CacheApi.TryGetInformation<List<Action>>("CheckpointCleanupKey", out var information, "EndskApi", logNotFound: false)) { return; } foreach (Action item in information) { item(); } } } public static class EnemyDamageBasePatchApi { public delegate void ReceiveMeleeDamage(Dam_EnemyDamageBase instance, ref pFullDamageData data); public delegate void ReceiveBulletDamage(Dam_EnemyDamageBase instance, ref pBulletDamageData data); public delegate void ReceiveExplosionDamage(Dam_EnemyDamageBase instance, PlayerAgent? source, ref pExplosionDamageData data); public delegate void ProcessReceivedDamage(Dam_EnemyDamageBase instance, ref float damage, Agent damageSource, ref Vector3 position, ref Vector3 direction, ref ES_HitreactType hitreact, ref bool tryForceHitreact, ref int limbID, ref float staggerDamageMulti, ref DamageNoiseLevel damageNoiseLevel); private static bool _setup; internal static List<(double, ReceiveMeleeDamage)> MeleePrefixCallbacks { get { return CacheApi.GetInstance<List<(double, ReceiveMeleeDamage)>>("EndskApi"); } set { CacheApi.SaveInstance(value, "EndskApi"); } } internal static List<(double, ReceiveMeleeDamage)> MeleePostfixCallbacks { get { return CacheApi.GetInstance<List<(double, ReceiveMeleeDamage)>>(); } set { CacheApi.SaveInstance(value); } } internal static List<(double, ReceiveBulletDamage)> BulletPrefixCallbacks { get { return CacheApi.GetInstance<List<(double, ReceiveBulletDamage)>>("EndskApi"); } set { CacheApi.SaveInstance(value, "EndskApi"); } } internal static List<(double, ReceiveBulletDamage)> BulletPostfixCallbacks { get { return CacheApi.GetInstance<List<(double, ReceiveBulletDamage)>>(); } set { CacheApi.SaveInstance(value); } } internal static List<(double, ReceiveExplosionDamage)> ExplosionPrefixCallbacks { get { return CacheApi.GetInstance<List<(double, ReceiveExplosionDamage)>>("EndskApi"); } set { CacheApi.SaveInstance(value, "EndskApi"); } } internal static List<(double, ReceiveExplosionDamage)> ExplosionPostfixCallbacks { get { return CacheApi.GetInstance<List<(double, ReceiveExplosionDamage)>>(); } set { CacheApi.SaveInstance(value); } } internal static void Setup() { if (!_setup) { BepInExLoader.Harmony.PatchAll(typeof(EnemyDamageBasePatches)); BepInExLoader.Harmony.PatchAll(typeof(MineDeployerExplosivePatches)); BepInExLoader.Harmony.PatchAll(typeof(EnemyTagPatches)); MeleePrefixCallbacks = new List<(double, ReceiveMeleeDamage)>(); MeleePostfixCallbacks = new List<(double, ReceiveMeleeDamage)>(); BulletPrefixCallbacks = new List<(double, ReceiveBulletDamage)>(); BulletPostfixCallbacks = new List<(double, ReceiveBulletDamage)>(); ExplosionPrefixCallbacks = new List<(double, ReceiveExplosionDamage)>(); ExplosionPostfixCallbacks = new List<(double, ReceiveExplosionDamage)>(); _setup = true; } } public static void AddReceiveMeleePrefix(double priority, ReceiveMeleeDamage method) { Setup(); MeleePrefixCallbacks.Add((priority, method)); MeleePrefixCallbacks = new List<(double, ReceiveMeleeDamage)>(MeleePrefixCallbacks.OrderBy<(double, ReceiveMeleeDamage), double>(((double, ReceiveMeleeDamage) x) => x.Item1)); } public static void AddReceiveMeleePostfix(double priority, ReceiveMeleeDamage method) { Setup(); MeleePostfixCallbacks.Add((priority, method)); MeleePostfixCallbacks = new List<(double, ReceiveMeleeDamage)>(MeleePostfixCallbacks.OrderBy<(double, ReceiveMeleeDamage), double>(((double, ReceiveMeleeDamage) x) => x.Item1)); } public static void AddReceiveBulletPrefix(double priority, ReceiveBulletDamage method) { Setup(); BulletPrefixCallbacks.Add((priority, method)); BulletPrefixCallbacks = new List<(double, ReceiveBulletDamage)>(BulletPrefixCallbacks.OrderBy<(double, ReceiveBulletDamage), double>(((double, ReceiveBulletDamage) x) => x.Item1)); } public static void AddReceiveBulletPostfix(double priority, ReceiveBulletDamage method) { Setup(); BulletPostfixCallbacks.Add((priority, method)); BulletPostfixCallbacks = new List<(double, ReceiveBulletDamage)>(BulletPostfixCallbacks.OrderBy<(double, ReceiveBulletDamage), double>(((double, ReceiveBulletDamage) x) => x.Item1)); } public static void AddReceiveExplosionPrefix(double priority, ReceiveExplosionDamage method) { Setup(); ExplosionPrefixCallbacks.Add((priority, method)); ExplosionPrefixCallbacks = new List<(double, ReceiveExplosionDamage)>(ExplosionPrefixCallbacks.OrderBy<(double, ReceiveExplosionDamage), double>(((double, ReceiveExplosionDamage) x) => x.Item1)); } public static void AddReceiveExplosionPostfix(double priority, ReceiveExplosionDamage method) { Setup(); ExplosionPostfixCallbacks.Add((priority, method)); ExplosionPostfixCallbacks = new List<(double, ReceiveExplosionDamage)>(ExplosionPostfixCallbacks.OrderBy<(double, ReceiveExplosionDamage), double>(((double, ReceiveExplosionDamage) x) => x.Item1)); } internal static void InvokeReceiveMeleePrefix(Dam_EnemyDamageBase instance, ref pFullDamageData data) { foreach (var meleePrefixCallback in MeleePrefixCallbacks) { meleePrefixCallback.Item2(instance, ref data); } } internal static void InvokeReceiveMeleePostfix(Dam_EnemyDamageBase instance, ref pFullDamageData data) { foreach (var meleePostfixCallback in MeleePostfixCallbacks) { meleePostfixCallback.Item2(instance, ref data); } } internal static void InvokeReceiveBulletPrefix(Dam_EnemyDamageBase instance, ref pBulletDamageData data) { foreach (var bulletPrefixCallback in BulletPrefixCallbacks) { bulletPrefixCallback.Item2(instance, ref data); } } internal static void InvokeReceiveBulletPostfix(Dam_EnemyDamageBase instance, ref pBulletDamageData data) { foreach (var bulletPostfixCallback in BulletPostfixCallbacks) { bulletPostfixCallback.Item2(instance, ref data); } } internal static void InvokeReceiveExplosionPrefix(Dam_EnemyDamageBase instance, ref pExplosionDamageData data) { foreach (var explosionPrefixCallback in ExplosionPrefixCallbacks) { explosionPrefixCallback.Item2(instance, MineDeployerExplosivePatches.CachedAgent, ref data); } } internal static void InvokeReceiveExplosionPostfix(Dam_EnemyDamageBase instance, ref pExplosionDamageData data) { foreach (var explosionPostfixCallback in ExplosionPostfixCallbacks) { explosionPostfixCallback.Item2(instance, MineDeployerExplosivePatches.CachedAgent, ref data); } } } public static class EnemyKillApi { private const string EnemyKillKey = "EnemyKillCallbacks"; private static bool _setup = false; private static Dictionary<IntPtr, bool> _enemyStates = new Dictionary<IntPtr, bool>(); private static Dictionary<IntPtr, EnemyKillDistribution> _enemyDistributions = new Dictionary<IntPtr, EnemyKillDistribution>(); public static void AddEnemyKilledCallback(Action<EnemyKillDistribution> callBack) { Setup(); if (!CacheApi.TryGetInformation<List<Action<EnemyKillDistribution>>>("EnemyKillCallbacks", out var information, "EndskApi", logNotFound: false)) { information = new List<Action<EnemyKillDistribution>>(); CacheApi.SaveInformation("EnemyKillCallbacks", information, "EndskApi"); } information.Add(callBack); } internal static void InvokeEnemyKilledCallbacks(EnemyKillDistribution enemyKill) { if (!CacheApi.TryGetInformation<List<Action<EnemyKillDistribution>>>("EnemyKillCallbacks", out var information, "EndskApi", logNotFound: false)) { return; } foreach (Action<EnemyKillDistribution> item in information) { item(enemyKill); } } private static void Setup() { if (!_setup) { EnemyDamageBasePatchApi.AddReceiveMeleePrefix(1.0, ReceiveMeleePrefix); EnemyDamageBasePatchApi.AddReceiveMeleePostfix(1.0, ReceiveMeleePostfix); EnemyDamageBasePatchApi.AddReceiveBulletPrefix(1.0, ReceiveBulletPrefix); EnemyDamageBasePatchApi.AddReceiveBulletPostfix(1.0, ReceiveBulletPostfix); EnemyDamageBasePatchApi.AddReceiveExplosionPrefix(1.0, ReceiveExplosionPrefix); EnemyDamageBasePatchApi.AddReceiveExplosionPostfix(1.0, ReceiveExplosionPostfix); LevelApi.AddEndLevelCallback(delegate { _enemyStates.Clear(); _enemyDistributions.Clear(); }); _setup = true; } } private static void ReceiveMeleePrefix(Dam_EnemyDamageBase instance, ref pFullDamageData data) { bool alive = ((Agent)instance.Owner).Alive; if (alive || _enemyStates.ContainsKey(((Il2CppObjectBase)instance).Pointer)) { _enemyStates[((Il2CppObjectBase)instance).Pointer] = alive; } } private static void ReceiveMeleePostfix(Dam_EnemyDamageBase instance, ref pFullDamageData data) { if (!(_enemyStates.TryGetValue(((Il2CppObjectBase)instance).Pointer, out var value) && value)) { return; } Agent val = default(Agent); ((pAgent)(ref data.source)).TryGet(ref val); if (!((Object)(object)val == (Object)null)) { PlayerAgent val2 = ((Il2CppObjectBase)val).TryCast<PlayerAgent>(); DamageDistributionAddDamageDealt(instance.Owner, val2, ((UFloat16)(ref data.damage)).Get(((Dam_SyncedDamageBase)instance).HealthMax)); if (!((Agent)instance.Owner).Alive) { EnemyDied(instance.Owner, val2, LastHitType.Melee); } } } private static void ReceiveBulletPrefix(Dam_EnemyDamageBase instance, ref pBulletDamageData data) { bool alive = ((Agent)instance.Owner).Alive; if (alive || _enemyStates.ContainsKey(((Il2CppObjectBase)instance).Pointer)) { _enemyStates[((Il2CppObjectBase)instance).Pointer] = alive; } } private static void ReceiveBulletPostfix(Dam_EnemyDamageBase instance, ref pBulletDamageData data) { if (!(_enemyStates.TryGetValue(((Il2CppObjectBase)instance).Pointer, out var value) && value)) { return; } Agent val = default(Agent); ((pAgent)(ref data.source)).TryGet(ref val); if (!((Object)(object)val == (Object)null)) { PlayerAgent val2 = ((Il2CppObjectBase)val).TryCast<PlayerAgent>(); DamageDistributionAddDamageDealt(instance.Owner, val2, ((UFloat16)(ref data.damage)).Get(((Dam_SyncedDamageBase)instance).HealthMax)); if (!((Agent)instance.Owner).Alive) { EnemyDied(instance.Owner, val2, LastHitType.ShootyWeapon); } } } private static void ReceiveExplosionPrefix(Dam_EnemyDamageBase instance, PlayerAgent? source, ref pExplosionDamageData data) { bool alive = ((Agent)instance.Owner).Alive; if (alive || _enemyStates.ContainsKey(((Il2CppObjectBase)instance).Pointer)) { _enemyStates[((Il2CppObjectBase)instance).Pointer] = alive; } } private static void ReceiveExplosionPostfix(Dam_EnemyDamageBase instance, PlayerAgent? source, ref pExplosionDamageData data) { if (_enemyStates.TryGetValue(((Il2CppObjectBase)instance).Pointer, out var value) && value) { if ((Object)(object)source != (Object)null) { DamageDistributionAddDamageDealt(instance.Owner, source, ((UFloat16)(ref data.damage)).Get(((Dam_SyncedDamageBase)instance).HealthMax)); } if (!((Agent)instance.Owner).Alive) { EnemyDied(instance.Owner, source, LastHitType.Explosion); } } } private static void DamageDistributionAddDamageDealt(EnemyAgent hitEnemy, PlayerAgent damageDealer, float damageDealt) { if (!_enemyDistributions.TryGetValue(((Il2CppObjectBase)hitEnemy).Pointer, out EnemyKillDistribution value)) { _enemyDistributions.Add(((Il2CppObjectBase)hitEnemy).Pointer, value = new EnemyKillDistribution(hitEnemy)); } value.AddDamageDealtByPlayerAgent(damageDealer, damageDealt); } private static void EnemyDied(EnemyAgent hitEnemy, PlayerAgent? lastHit, LastHitType lastHitType) { if (_enemyDistributions.TryGetValue(((Il2CppObjectBase)hitEnemy).Pointer, out EnemyKillDistribution value)) { value.LastHitDealtBy = lastHit; value.lastHitType = lastHitType; InvokeEnemyKilledCallbacks(value); _enemyDistributions.Remove(((Il2CppObjectBase)hitEnemy).Pointer); } _enemyStates.Remove(((Il2CppObjectBase)hitEnemy).Pointer); } public static void RegisterDamage(EnemyAgent enemy, PlayerAgent? source, float damage, bool willKill) { if ((Object)(object)source != (Object)null) { DamageDistributionAddDamageDealt(enemy, source, damage); } if (willKill) { EnemyDied(enemy, source, LastHitType.ShootyWeapon); } } public static void RegisterBiotag(EnemyAgent enemy, PlayerAgent source) { if (!_enemyDistributions.TryGetValue(((Il2CppObjectBase)enemy).Pointer, out EnemyKillDistribution value)) { _enemyDistributions.Add(((Il2CppObjectBase)enemy).Pointer, value = new EnemyKillDistribution(enemy)); } value.AddPlayerTagged(source); } } public static class GearSwitchApi { static GearSwitchApi() { LevelApi.AddEndLevelCallback(LevelEndedCallback); SetGearInfoCache(new Dictionary<IntPtr, GearInfo>()); } public static GearInfo CreateNewWeaponInfo(InventorySlot slot, AmmoType ammoType, GearIDRange gearId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Invalid comparison between Unknown and I4 PlayerDataBlock block = GameDataBlockBase<PlayerDataBlock>.GetBlock(1u); InventorySlotAmmo val = new InventorySlotAmmo(); val.Slot = slot; val.AmmoInPack = (((int)ammoType == 1) ? block.AmmoSpecialInitial : block.AmmoStandardInitial); val.AmmoMaxCap = (((int)ammoType == 1) ? block.AmmoSpecialMaxCap : block.AmmoStandardMaxCap); GearInfo gearInfo = new GearInfo(gearId, 0, val); GearCategoryDataBlock block2 = GameDataBlockBase<GearCategoryDataBlock>.GetBlock(gearId.GetCompID((eGearComponent)2)); ArchetypeDataBlock block3 = GameDataBlockBase<ArchetypeDataBlock>.GetBlock(GearBuilder.GetArchetypeID(block2, (eWeaponFireMode)gearId.GetCompID((eGearComponent)1))); gearInfo.InventorySlotAmmo.BulletClipSize = block3.DefaultClipSize; gearInfo.AmmunitionInMagazine = block3.DefaultClipSize; LogManager.Debug($"Putting {gearInfo.AmmunitionInMagazine} into {block2.PublicName}"); return gearInfo; } public static void EquipGear(GearInfo newGear, out GearInfo oldGear) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) AddWeaponInfo(newGear); SyncAmmonitionWithRegisteredWeapon(newGear.Slot, out oldGear); EquipGear(newGear); } public static void SyncAmmonitionWithRegisteredWeapon(InventorySlot slot, out GearInfo changedWeapon) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (TryGetItemEquippableInSlot(slot, out ItemEquippable item) && (Object)(object)item != (Object)null) { LogManager.Debug("SyncAmmunitionWithRegisteredWeapon actually found Weapon itemequippable"); InventorySlotAmmo inventorySlotAmmo = PlayerBackpackManager.LocalBackpack.AmmoStorage.GetInventorySlotAmmo(slot); if (GetGearInfo().TryGetValue(((Il2CppObjectBase)inventorySlotAmmo).Pointer, out changedWeapon)) { changedWeapon.AmmunitionInMagazine = item.GetCurrentClip(); LogManager.Debug($"Updated Ammo to {changedWeapon.AmmunitionInMagazine}, Max is {changedWeapon.InventorySlotAmmo.BulletClipSize}"); } } else { changedWeapon = null; } } public static void AddWeaponInfo(GearInfo info) { Dictionary<IntPtr, GearInfo> gearInfo = GetGearInfo(); if (!gearInfo.ContainsKey(((Il2CppObjectBase)info.InventorySlotAmmo).Pointer)) { gearInfo.Add(((Il2CppObjectBase)info.InventorySlotAmmo).Pointer, info); } } public static void UnregisterWeapon(GearInfo info) { GetGearInfo().Remove(((Il2CppObjectBase)info.InventorySlotAmmo).Pointer); } private static void EquipGear(GearInfo info) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) PlayerBackpack localBackpack = PlayerBackpackManager.LocalBackpack; localBackpack.SpawnAndEquipGearAsync(info.Slot, info.GearId, delBackpackItemCallback.op_Implicit((Action<BackpackItem>)BackPackItemCreatedCallBack)); SetAmmoStorage(info, localBackpack.AmmoStorage); void BackPackItemCreatedCallBack(BackpackItem backpackItem) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) ItemEquippable val = ((Il2CppObjectBase)backpackItem.Instance).TryCast<ItemEquippable>(); if ((Object)(object)val != (Object)null) { val.SetCurrentClip(info.AmmunitionInMagazine); PlayerManager.GetLocalPlayerAgent().Sync.WantsToWieldSlot(info.Slot, false); } } } private static bool TryGetItemEquippableInSlot(InventorySlot slot, out ItemEquippable item) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) BackpackItem val = default(BackpackItem); if (!PlayerBackpackManager.LocalBackpack.TryGetBackpackItem(slot, ref val)) { item = null; return false; } item = ((Il2CppObjectBase)val.Instance).TryCast<ItemEquippable>(); return (Object)(object)item != (Object)null; } private static void SetAmmoStorage(GearInfo info, PlayerAmmoStorage storage) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected I4, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 ((Il2CppArrayBase<InventorySlotAmmo>)(object)storage.m_ammoStorage)[(int)info.AmmoType] = info.InventorySlotAmmo; AmmoType ammoType = info.AmmoType; if ((int)ammoType != 0) { if ((int)ammoType == 1) { storage.SpecialAmmo = info.InventorySlotAmmo; } } else { storage.StandardAmmo = info.InventorySlotAmmo; } } internal static void LevelEndedCallback() { SetGearInfoCache(new Dictionary<IntPtr, GearInfo>()); } private static Dictionary<IntPtr, GearInfo> GetGearInfo() { return CacheApi.GetInstance<Dictionary<IntPtr, GearInfo>>("EndskApi"); } private static void SetGearInfoCache(Dictionary<IntPtr, GearInfo> dic) { CacheApi.SaveInstance(dic, "EndskApi"); } } public static class InitApi { private const string InitKey = "InitKey"; static InitApi() { BepInExLoader.Harmony.PatchAll(typeof(PageRundownNewPatches)); BepInExLoader.Harmony.PatchAll(typeof(GUIManagerPatches)); } public static void AddInitCallback(Action callBack) { if (!CacheApi.TryGetInformation<List<Action>>("InitKey", out var information, "EndskApi", logNotFound: false)) { information = new List<Action>(); CacheApi.SaveInformation("InitKey", information, "EndskApi"); } information.Add(callBack); } internal static void InvokeInitCallbacks() { if (!CacheApi.TryGetInformation<List<Action>>("InitKey", out var information, "EndskApi", logNotFound: false)) { return; } foreach (Action item in information) { try { item(); } catch (Exception ex) { LogManager.Error(ex.ToString()); } } CacheApi.RemoveInformation("InitKey", "EndskApi"); } } public static class LevelApi { private const string EndLevelKey = "EndLevelKey"; private const string StartLevelKey = "StartLevelKey"; static LevelApi() { BepInExLoader.Harmony.PatchAll(typeof(GsAfterLevelPatches)); BepInExLoader.Harmony.PatchAll(typeof(GsInLevelPatches)); } public static void AddBeginLevelCallback(Action callBack) { if (!CacheApi.TryGetInformation<List<Action>>("StartLevelKey", out var information, "EndskApi", logNotFound: false)) { information = new List<Action>(); CacheApi.SaveInformation("StartLevelKey", information, "EndskApi"); } information.Add(callBack); } public static void AddEndLevelCallback(Action callBack) { if (!CacheApi.TryGetInformation<List<Action>>("EndLevelKey", out var information, "EndskApi", logNotFound: false)) { information = new List<Action>(); CacheApi.SaveInformation("EndLevelKey", information, "EndskApi"); } information.Add(callBack); } internal static void InvokeEndLevelCallbacks() { if (!CacheApi.TryGetInformation<List<Action>>("EndLevelKey", out var information, "EndskApi", logNotFound: false)) { return; } foreach (Action item in information) { try { item(); } catch (Exception msg) { LogManager.Error(msg); } } } internal static void InvokeStartLevelCallbacks() { if (!CacheApi.TryGetInformation<List<Action>>("StartLevelKey", out var information, "EndskApi", logNotFound: false)) { return; } foreach (Action item in information) { try { item(); } catch (Exception msg) { LogManager.Error(msg); } } } } public static class MenuApi { private static bool _setup; static MenuApi() { } internal static void Setup() { //IL_000c: 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) //IL_0026: Expected O, but got Unknown if (!_setup) { GameObject val = new GameObject("EndskApi_Menu"); CacheApi.SaveInstance(val.AddComponent<MainMenu>(), "EndskApi"); Object.DontDestroyOnLoad((Object)val); _setup = true; } } public static void ActivateUnknownMenu<TScript>(TScript menu) where TScript : BaseMenu { Setup(); CacheApi.GetInstance<MainMenu>("EndskApi").ActivateUnknownMenu(menu); } public static TScript AddMenu<TScript>(string pageName) where TScript : BaseMenu { Setup(); MainMenu instance = CacheApi.GetInstance<MainMenu>("EndskApi"); TScript val = ((Component)instance).gameObject.AddComponent<TScript>(); instance.AddPage(pageName, val); CacheApi.SaveInstance(val, "EndskApi"); return val; } public static TScript GetMenu<TScript>() where TScript : BaseMenu { Setup(); return CacheApi.GetInstance<TScript>("EndskApi"); } public static void AddMiddleMouseClickTool(IExtendedTool tool) { Setup(); CacheApi.GetInstance<MainMenu>("EndskApi").AddMiddleMouseClickTool(tool); } public static void RemoveMiddleMouseClickTool(IExtendedTool tool) { Setup(); CacheApi.GetInstance<MainMenu>("EndskApi").RemoveMiddleMouseClickTool(tool); } public static void AddTimerTool(IExtendedTool tool) { Setup(); CacheApi.GetInstance<MainMenu>("EndskApi").AddTimedTool(tool); } public static void RemoveTimerTool(IExtendedTool tool) { Setup(); CacheApi.GetInstance<MainMenu>("EndskApi").RemoveTimedTool(tool); } public static void AddHiddenFunction(Tool tool) { Setup(); CacheApi.GetInstance<MainMenu>("EndskApi").AddHiddenTool(tool); } public static void RemoveHiddenFunction(Tool tool) { Setup(); CacheApi.GetInstance<MainMenu>("EndskApi").RemoveHiddenTool(tool); } } public static class ProfileIndependentDataApi { private static readonly string _localLowPath; static ProfileIndependentDataApi() { _localLowPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData).Replace("Roaming", "LocalLow"), "10 Chambers Collective", "Mods"); if (!Directory.Exists(_localLowPath)) { Directory.CreateDirectory(_localLowPath); } } public static T Load<T>(string fileName, JsonSerializerOptions option = null) where T : new() { if (TryLoadFromCache<T>(fileName, out var cachedObj)) { return cachedObj; } if (option == null) { option = new JsonSerializerOptions { IncludeFields = false, ReadCommentHandling = JsonCommentHandling.Skip, PropertyNameCaseInsensitive = true, WriteIndented = true }; } if (File.Exists(CreatePath(fileName))) { return JsonSerializer.Deserialize<T>(File.ReadAllText(CreatePath(fileName)), option); } return new T(); } public static void Save(object obj, string fileName, JsonSerializerOptions option = null) { SaveIntoCache(fileName, obj); if (option == null) { option = new JsonSerializerOptions { IncludeFields = false, ReadCommentHandling = JsonCommentHandling.Skip, PropertyNameCaseInsensitive = true, WriteIndented = true }; } File.WriteAllText(CreatePath(fileName), JsonSerializer.Serialize(obj, option)); } internal static bool TryLoadFromCache<T>(string fileName, out T cachedObj) { return CacheApi.TryGetInformation<T>(fileName, out cachedObj, "ProfileIndependentApi", logNotFound: false); } internal static void SaveIntoCache(string fileName, object obj) { CacheApi.SaveInformation(fileName, obj, "ProfileIndependentApi"); } private static string CreatePath(string fileName) { if (fileName.EndsWith(".json")) { return Path.Combine(_localLowPath, fileName); } return Path.Combine(_localLowPath, fileName + ".json"); } } }
Expansions.dll
Decompiled 2 days agousing System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.Json; using System.Text.Json.Serialization; using AIGraph; using AK; using Agents; using BepInEx; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using CullingSystem; using EndskApi.Api; using Enemies; using Expansions.Information.PackConverter; using Expansions.Information.ToolAbility; using Expansions.Patches.DoubleJump; using Expansions.Patches.PackConverter; using Expansions.Patches.ToolAbility; using GTFO.API; using GTFuckingXP.Communication; using GTFuckingXP.Extensions; using GTFuckingXP.Information.Level; using GTFuckingXP.Managers; using GameData; using HarmonyLib; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Localization; using MTFO.Managers; using Microsoft.CodeAnalysis; using Player; using SNetwork; using UnityEngine; using XpExpansions.Extensions; using XpExpansions.Information; using XpExpansions.Information.BioTrackerLocal; using XpExpansions.Information.DoubleJump; using XpExpansions.Information.StartingXp; using XpExpansions.Manager; using XpExpansions.Scripts; using XpExpansions.Util; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Expansions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+gitfbd117d-dirty-master.fbd117d34bdc37962630c75f377a07297f961609")] [assembly: AssemblyProduct("Expansions")] [assembly: AssemblyTitle("Expansions")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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; } } } namespace Expansions { [GeneratedCode("VersionInfoGenerator", "2.1.3+git35c0c2a-master")] [CompilerGenerated] internal static class VersionInfo { public const string RootNamespace = "Expansions"; public const string Version = "1.0.0"; public const string VersionPrerelease = null; public const string VersionMetadata = "gitfbd117d-dirty-master"; public const string SemVer = "1.0.0+gitfbd117d-dirty-master"; public const string GitRevShort = "fbd117d-dirty"; public const string GitRevLong = "fbd117d34bdc37962630c75f377a07297f961609-dirty"; public const string GitBranch = "master"; public const string GitTag = null; public const int GitCommitsSinceTag = 0; public const bool GitIsDirty = true; } } namespace Expansions.Patches.ToolAbility { internal static class MinePatches { [HarmonyPatch(typeof(MineDeployerFirstPerson), "OnWield")] [HarmonyPostfix] [HarmonyWrapSafe] private static void OnDeployerSpawn(MineDeployerFirstPerson __instance) { if (((Agent)((Item)__instance).Owner).IsLocallyOwned && !__instance.m_isConsumable && ToolAbilityHandler.Exists) { ToolAbilityHandler.Instance.OnDeployerWield(__instance); } } [HarmonyPatch(typeof(MineDeployerFirstPerson), "OnStickyMineSpawned")] [HarmonyPostfix] [HarmonyAfter(new string[] { "Dinorush.ExtraToolCustomization" })] [HarmonyWrapSafe] private static void OnMineSpawn(MineDeployerFirstPerson __instance, ISyncedItem item) { if (ToolAbilityHandler.Exists && !__instance.m_isConsumable) { ToolAbilityHandler.Instance.OnMineSpawn(((Il2CppObjectBase)item).Cast<MineDeployerInstance>()); } } [HarmonyPatch(typeof(MineDeployerInstance), "OnDestroy")] [HarmonyPrefix] [HarmonyWrapSafe] private static void OnMineDespawn(MineDeployerInstance __instance) { if (ToolAbilityHandler.Exists) { PlayerAgent owner = ((Item)__instance).Owner; if (owner != null && ((Agent)owner).IsLocallyOwned) { ToolAbilityHandler.Instance.OnMineDespawn(__instance); } } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPrefix] [HarmonyWrapSafe] private static bool PreMineCanWield(MineDeployerFirstPerson __instance, ref bool __result) { if (ToolAbilityHandler.InMineMode && !__instance.m_isConsumable) { __result = true; return false; } return true; } [HarmonyPatch(typeof(MineDeployerFirstPerson), "Update")] [HarmonyPrefix] [HarmonyWrapSafe] private static void OnMineUpdate(MineDeployerFirstPerson __instance, ref (IInteractable interact, bool ladder) __state) { if (ToolAbilityHandler.MovingMine) { PlayerInteraction interaction = ((Item)__instance).Owner.Interaction; __state = (interact: interaction.m_bestSelectedInteract, ladder: interaction.m_enterLadderVisible); interaction.m_enterLadderVisible = false; interaction.m_bestSelectedInteract = null; } } [HarmonyPatch(typeof(MineDeployerFirstPerson), "Update")] [HarmonyPostfix] [HarmonyWrapSafe] private static void OnMineUpdate(MineDeployerFirstPerson __instance, (IInteractable interact, bool ladder) __state) { if (ToolAbilityHandler.MovingMine) { __instance.m_placementIndicator.SetVisible(true); FirstPersonItemHolder fPItemHolder = ((ItemEquippable)__instance).FPItemHolder; bool itemHiddenTrigger = fPItemHolder.ItemHiddenTrigger; fPItemHolder.ItemHiddenTrigger = false; __instance.m_placementIndicator.SetPlacementEnabled(ToolAbilityHandler.CheckCanPlaceIgnoreHidden(__instance)); fPItemHolder.ItemHiddenTrigger = itemHiddenTrigger; PlayerInteraction interaction = ((Item)__instance).Owner.Interaction; interaction.m_enterLadderVisible = __state.ladder; interaction.m_bestSelectedInteract = __state.interact; } } } internal static class PlayerPatches { [HarmonyPatch(typeof(LocalPlayerAgent), "Setup")] [HarmonyPostfix] [HarmonyWrapSafe] private static void OnPlayerSpawn(LocalPlayerAgent __instance) { if ((Object)(object)((Component)__instance).GetComponent<ToolAbilityHandler>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<ToolAbilityHandler>(); } } } internal static class SentryPatches { [HarmonyPatch(typeof(SentryGunInstance), "OnGearSpawnComplete")] [HarmonyPostfix] [HarmonyAfter(new string[] { "Dinorush.ExtraToolCustomization" })] [HarmonyWrapSafe] private static void OnSentrySpawn(SentryGunInstance __instance) { if (ToolAbilityHandler.Exists) { PlayerAgent owner = ((Item)__instance).Owner; if (owner != null && ((Agent)owner).IsLocallyOwned) { ToolAbilityHandler.Instance.OnSentrySpawn(__instance); } } } [HarmonyPatch(typeof(SentryGunInstance), "OnDestroy")] [HarmonyPrefix] [HarmonyWrapSafe] private static void OnSentryDespawn(SentryGunInstance __instance) { if (ToolAbilityHandler.Exists) { PlayerAgent owner = ((Item)__instance).Owner; if (owner != null && ((Agent)owner).IsLocallyOwned) { ToolAbilityHandler.Instance.OnSentryDespawn(); } } } } } namespace Expansions.Patches.PackConverter { internal static class ResourcePatches { [HarmonyPatch(typeof(PlayerAgent), "GiveAmmoRel")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_GiveAmmo(PlayerAgent __instance, float ammoStandardRel, float ammoSpecialRel, float ammoClassRel) { PackConverterManager.ApplyConversions(__instance, ammoStandardRel, ammoSpecialRel, ammoClassRel); } [HarmonyPatch(typeof(PlayerAgent), "GiveHealth")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_GiveHealth(PlayerAgent __instance, float amountRel) { PackConverterManager.ApplyConversions(__instance, ResourceType.Health, amountRel); } [HarmonyPatch(typeof(PlayerAgent), "GiveDisinfection")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_GiveDisinfect(PlayerAgent __instance, float amountRel) { PackConverterManager.ApplyConversions(__instance, ResourceType.Disinfect, amountRel); } } } namespace Expansions.Patches.DoubleJump { [HarmonyPatch] internal static class FallDamagePatches { [HarmonyPrefix] [HarmonyPatch(typeof(Dam_SyncedDamageBase), "FallDamage")] private static bool FallDamage(Dam_SyncedDamageBase __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Invalid comparison between Unknown and I4 if ((int)__instance.DamageBaseOwner != 0) { return true; } PlayerAgent val = ((Il2CppObjectBase)__instance.GetBaseAgent()).Cast<PlayerAgent>(); if ((int)val.Locomotion.m_currentStateEnum == 3 || (int)val.Locomotion.m_currentStateEnum == 4) { return false; } return true; } } [HarmonyPatch] internal static class PLOC_Patches { private static bool _canDoubleJump = true; [HarmonyPostfix] [HarmonyPatch(typeof(PLOC_Crouch), "Enter")] [HarmonyPatch(typeof(PLOC_Run), "Enter")] [HarmonyPatch(typeof(PLOC_Stand), "Enter")] public static void Enter() { _canDoubleJump = DoubleJumpManager.DoubleJumpUnlocked; } [HarmonyPostfix] [HarmonyPatch(typeof(PLOC_Fall), "Update")] public static void Update(PLOC_Fall __instance) { TryDoubleJump((PLOC_Base)(object)__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(PLOC_Jump), "Update")] public static void Update(PLOC_Jump __instance) { TryDoubleJump((PLOC_Base)(object)__instance); } private static void TryDoubleJump(PLOC_Base state) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) PlayerAgent owner = state.m_owner; if (_canDoubleJump && InputMapper.GetButtonDown.Invoke((InputAction)5, owner.InputFilter)) { owner.Sound.Post(EVENTS.FLAMERFIREBURST, true); state.UpdateHorizontalVelocityOnGround(owner.PlayerData.runMoveSpeed); owner.Locomotion.ChangeState((PLOC_State)3, false); _canDoubleJump = false; } } } [HarmonyPatch] internal static class SuperJumpPatches { [HarmonyPostfix] [HarmonyPatch(typeof(PLOC_Jump), "Enter")] public static void Enter(PLOC_Jump __instance) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) PlayerAgent owner = ((PLOC_Base)__instance).m_owner; if (DoubleJumpManager.DoubleJumpUnlocked && InputMapper.GetButtonKeyMouse((InputAction)10, owner.InputFilter)) { owner.Sound.Post(EVENTS.IMPLANTSMALLJUMPBOOSTTRIGGER, true); PlayerLocomotion locomotion = owner.Locomotion; locomotion.VerticalVelocity += Vector3.up * DoubleJumpManager.ActiveConfig.SuperJumpVelocity; } } } } namespace Expansions.Information.ToolAbility { public class ToolAbilityHandler : MonoBehaviour { private enum ToolMode { None, Sentry, Mine } private class ProgressGradient { private const float WidthBuffer = 50f; private static Sprite s_sprite; private readonly GameObject _overlayGO; private readonly GameObject _maskGO; private readonly (float x, float y) _size; private static Sprite Sprite { get { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown if ((Object)(object)s_sprite != (Object)null) { return s_sprite; } Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, Color.white); val.Apply(); s_sprite = Sprite.Create(val, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 1f); return s_sprite; } } public ProgressGradient(SpriteRenderer orig) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown _overlayGO = new GameObject("ProgressOverlay"); _overlayGO.transform.SetParent(((Component)orig).transform, false); _overlayGO.layer = LayerManager.LAYER_UI; SpriteRenderer obj = _overlayGO.AddComponent<SpriteRenderer>(); Color color = orig.color; color.g = 1f; color.a = 0.2f; obj.sprite = orig.sprite; obj.color = color; ((Renderer)obj).sortingLayerID = ((Renderer)orig).sortingLayerID; ((Renderer)obj).sortingOrder = ((Renderer)orig).sortingOrder + 1; obj.drawMode = orig.drawMode; obj.tileMode = orig.tileMode; obj.spriteSortPoint = orig.spriteSortPoint; _size = (x: orig.size.x + 50f, y: orig.size.y); obj.size = new Vector2(_size.x, _size.y); obj.maskInteraction = (SpriteMaskInteraction)1; _maskGO = new GameObject("ProgressMask"); _maskGO.transform.SetParent(((Component)orig).transform, false); _maskGO.layer = LayerManager.LAYER_UI; SpriteMask obj2 = _maskGO.AddComponent<SpriteMask>(); obj2.sprite = Sprite; obj2.alphaCutoff = 0f; obj2.frontSortingLayerID = ((Renderer)orig).sortingLayerID; obj2.backSortingLayerID = ((Renderer)orig).sortingLayerID; obj2.frontSortingOrder = ((Renderer)orig).sortingOrder + 1; obj2.backSortingOrder = ((Renderer)orig).sortingOrder + 1; } public void Update(float progress) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) _maskGO.transform.localPosition = new Vector3(_size.x * (progress * 0.5f - 1f), 0f, 0f); _maskGO.transform.localScale = new Vector3(_size.x * progress, _size.y, 1f); } public void Destroy() { Object.Destroy((Object)(object)_maskGO); Object.Destroy((Object)(object)_overlayGO); } } private static PUI_InteractionPrompt s_interactPrompt; private PlayerAgent _player; private readonly CellSoundPlayer _sound = new CellSoundPlayer(); private ProgressGradient _slimGradient; private ProgressGradient _selectedGradient; private float _recallDuration = 1f; private float _recallDeltaMod; private bool _recallingTool; private float _toolRecallProgress; private Interact_Timed? _activeInteract; private ToolMode _mode; private SentryGunInstance? _sentry; private MineDeployerInstance? _movingMine; private MineDeployerFirstPerson? _mineDeployer; private Color _origMineColor = Color.black; private readonly Dictionary<IntPtr, MineDeployerInstance> _activeMines = new Dictionary<IntPtr, MineDeployerInstance>(); public static ToolAbilityHandler Instance { get; private set; } public static bool Exists { get; private set; } public static bool MovingMine { get { if (Exists) { return (Object)(object)Instance._movingMine != (Object)null; } return false; } } public static bool InMineMode { get { if (Exists) { return Instance.DoMineUpdate; } return false; } } public static bool InSentryMode { get { if (Exists) { return Instance.DoSentryUpdate; } return false; } } public float ToolRecallProgress { get { return _toolRecallProgress; } set { _toolRecallProgress = value; _slimGradient.Update(value); _selectedGradient.Update(value); if (_toolRecallProgress == 0f) { ((RectTransformComp)s_interactPrompt).SetVisible(false); } } } private bool DoSentryUpdate { get { if (_mode == ToolMode.Sentry) { return ToolAbilityManager.ActiveAbility.SentryRecall; } return false; } } private bool DoMineUpdate { get { ToolAbilityData.LevelData activeAbility = ToolAbilityManager.ActiveAbility; if (_mode == ToolMode.Mine && _activeMines.Count > 0) { if (!activeAbility.MineRecall) { return activeAbility.MoveMines; } return true; } return false; } } public static bool CheckCanPlaceIgnoreHidden(MineDeployerFirstPerson deployer) { FirstPersonItemHolder fPItemHolder = ((ItemEquippable)deployer).FPItemHolder; bool itemHiddenTrigger = fPItemHolder.ItemHiddenTrigger; fPItemHolder.ItemHiddenTrigger = false; bool result = deployer.CheckCanPlace(); fPItemHolder.ItemHiddenTrigger = itemHiddenTrigger; return result; } public ToolAbilityHandler(IntPtr ptr) : base(ptr) { }//IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) private void Awake() { //IL_0025: 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) if ((Object)(object)s_interactPrompt == (Object)null) { s_interactPrompt = ((Il2CppObjectBase)((GuiLayer)GuiManager.InteractionLayer).AddRectComp("Gui/Player/PUI_InteractionPrompt_CellUI", (GuiAnchor)3, new Vector2(0f, 0f), (Transform)null)).Cast<PUI_InteractionPrompt>(); ((Component)s_interactPrompt).transform.localPosition = new Vector3(0f, -360f, 0f); ((Component)((Component)s_interactPrompt).transform.GetChild(2)).gameObject.SetActive(false); ((Component)((Component)s_interactPrompt).transform.GetChild(3)).gameObject.SetActive(false); ((RectTransformComp)s_interactPrompt).SetVisible(false); } _player = ((Component)this).GetComponent<PlayerAgent>(); Instance = this; Exists = true; PUI_InventoryItem obj = GuiManager.PlayerLayer.Inventory.m_inventorySlots[(InventorySlot)3]; SpriteRenderer component = ((Component)obj.m_slim_root.transform.GetChild(0).GetChild(5)).GetComponent<SpriteRenderer>(); SpriteRenderer component2 = ((Component)obj.m_selected_root.transform.GetChild(0).GetChild(1)).GetComponent<SpriteRenderer>(); _slimGradient = new ProgressGradient(component); _selectedGradient = new ProgressGradient(component2); } private void Update() { if (DoSentryUpdate) { UpdateSentry(); } else if (DoMineUpdate) { UpdateMines(); } if (_recallingTool) { ToolRecallProgress = Math.Min(1f, ToolRecallProgress + Clock.Delta * _recallDeltaMod); } } private void UpdateSentry() { CheckToggleRecall(Text.Get(1419u), (InputAction)25); if (ToolRecallProgress == 1f) { StopRecall(finished: true); ToolAbilityManager.DoSentryPickup(_sentry); } } private void UpdateMines() { //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0153: 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_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_mineDeployer == (Object)null) { OnDeployerDespawn(); return; } bool isActiveAndEnabled = ((Behaviour)_mineDeployer).isActiveAndEnabled; if (ToolAbilityManager.ActiveAbility.MineRecall) { if (isActiveAndEnabled) { if (_recallingTool) { ((RectTransformComp)s_interactPrompt).SetVisible(true); } CheckToggleRecall(Text.Get(1420u), (InputAction)11); } else { ((RectTransformComp)s_interactPrompt).SetVisible(false); } } if (_recallingTool && ToolRecallProgress == 1f) { StopRecall(finished: true); foreach (MineDeployerInstance value in _activeMines.Values) { value.WantItemAction(((Item)value).Owner, (SyncedItemAction_New)0); } } if (!ToolAbilityManager.ActiveAbility.MoveMines) { return; } if ((Object)(object)_movingMine != (Object)null) { if (!isActiveAndEnabled || !InputMapper.GetButtonKeyMouse((InputAction)8, _player.InputFilter)) { PlayerInteraction interaction = _player.Interaction; bool enterLadderVisible = interaction.m_enterLadderVisible; IInteractable bestSelectedInteract = interaction.m_bestSelectedInteract; interaction.m_enterLadderVisible = false; interaction.m_bestSelectedInteract = null; if (CheckCanPlaceIgnoreHidden(_mineDeployer)) { MineDeployerInstance? movingMine = _movingMine; RaycastHit lastRayHit = _mineDeployer.m_lastRayHit; Vector3 point = ((RaycastHit)(ref lastRayHit)).point; lastRayHit = _mineDeployer.m_lastRayHit; ToolAbilityManager.DoMineMove(movingMine, point, Quaternion.LookRotation(((RaycastHit)(ref lastRayHit)).normal * -1f, ((Component)((Item)_mineDeployer).Owner).transform.forward)); } interaction.m_enterLadderVisible = enterLadderVisible; interaction.m_bestSelectedInteract = bestSelectedInteract; _mineDeployer.m_placementIndicator.m_colorEnabled = _origMineColor; _mineDeployer.m_placementIndicator.m_cookieLight.color = _origMineColor; _movingMine = null; } } else if (isActiveAndEnabled && InputMapper.GetButtonDownKeyMouse((InputAction)8, _player.InputFilter)) { Interact_Timed? activeInteract = _activeInteract; MonoBehaviour val = ((activeInteract != null) ? ((Il2CppObjectBase)activeInteract).TryCast<MonoBehaviour>() : null); if ((Object)(object)val != (Object)null) { _movingMine = ((Component)((Component)val).transform.parent).GetComponent<MineDeployerInstance>(); _mineDeployer.m_placementIndicator.m_colorEnabled = Color.cyan; } } } private void CheckToggleRecall(string toolName, InputAction toggleAction) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (!InputMapper.GetButtonDownKeyMouse(toggleAction, _player.InputFilter)) { return; } if (!_recallingTool) { _recallingTool = true; _sound.Post(EVENTS.INTERACT_TOOL_START, true); ((RectTransformComp)s_interactPrompt).SetVisible(true); s_interactPrompt.SetPrompt("Cancel " + toolName + " Recall", Text.Format(825u, (Object[])(object)new Object[1] { Object.op_Implicit(InputMapper.GetBindingName(toggleAction)) })); if (_mode == ToolMode.Sentry) { _recallDeltaMod = ToolAbilityManager.ActiveAbility.SentryRecallSpeed / _recallDuration; } else { _recallDeltaMod = ToolAbilityManager.ActiveAbility.MineRecallSpeed / _recallDuration; } } else { StopRecall(); } } private void StopRecall(bool finished = false) { if (_recallingTool) { _recallingTool = false; _sound.Post(finished ? EVENTS.INTERACT_TOOL_FINISHED : EVENTS.INTERACT_TOOL_CANCEL, true); ToolRecallProgress = 0f; ((RectTransformComp)s_interactPrompt).SetVisible(false); } } internal void OnSentrySpawn(SentryGunInstance sentry) { _mode = ToolMode.Sentry; _sentry = sentry; _activeInteract = ((Il2CppObjectBase)((Item)sentry).PickupInteraction).Cast<Interact_Timed>(); _recallDuration = _activeInteract.InteractDuration; } internal void OnSentryDespawn() { _mode = ToolMode.None; _sentry = null; _activeInteract = null; StopRecall(); } internal void OnDeployerWield(MineDeployerFirstPerson deployer) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_mineDeployer != (Object)null) || !(((Il2CppObjectBase)_mineDeployer).Pointer == ((Il2CppObjectBase)deployer).Pointer)) { _mode = ToolMode.Mine; _mineDeployer = deployer; _origMineColor = deployer.m_placementIndicator.m_colorEnabled; } } internal void OnDeployerDespawn() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_mineDeployer != (Object)null) { _mineDeployer.m_placementIndicator.m_colorEnabled = _origMineColor; } _mineDeployer = null; _activeInteract = null; StopRecall(); _activeMines.Clear(); } internal void OnMineSpawn(MineDeployerInstance instance) { Interact_Timed interact = ((Il2CppObjectBase)((Item)instance).PickupInteraction).Cast<Interact_Timed>(); Interact_Timed obj = interact; obj.OnInteractionSelected += Action<PlayerAgent, bool>.op_Implicit((Action<PlayerAgent, bool>)delegate(PlayerAgent player, bool selected) { if (selected) { _activeInteract = interact; } else { _activeInteract = null; } }); _recallDuration = interact.InteractDuration; _activeMines.TryAdd(((Il2CppObjectBase)instance).Pointer, instance); } internal void OnMineDespawn(MineDeployerInstance mine) { if (_activeMines.Remove(((Il2CppObjectBase)mine).Pointer) && _activeMines.Count == 0) { _activeInteract = null; StopRecall(); } } private void OnDestroy() { OnSentryDespawn(); OnDeployerDespawn(); _slimGradient.Destroy(); _selectedGradient.Destroy(); if ((Object)(object)Instance == (Object)(object)this) { Exists = false; Instance = null; } } } } namespace Expansions.Information.PackConverter { public class PackConverterData { public class LevelData { public class ConversionData { public ResourceType ReceiveType { get; set; } public ResourceType ConvertType { get; set; } public float Value { get; set; } } public int Level { get; set; } public List<ConversionData> Conversions { get; set; } = new List<ConversionData>(); } public static readonly PackConverterData[] Template = new PackConverterData[1] { new PackConverterData { DebugName = "Template", DataChanges = new List<LevelData> { new LevelData { Level = 0, Conversions = new List<LevelData.ConversionData> { new LevelData.ConversionData { ReceiveType = ResourceType.Ammo, ConvertType = ResourceType.Tool, Value = 0.5f } } }, new LevelData { Level = 10, Conversions = new List<LevelData.ConversionData> { new LevelData.ConversionData { ReceiveType = ResourceType.Standard, ConvertType = ResourceType.Tool, Value = 0.25f }, new LevelData.ConversionData { ReceiveType = ResourceType.Special, ConvertType = ResourceType.Tool, Value = 0.25f }, new LevelData.ConversionData { ReceiveType = ResourceType.Health, ConvertType = ResourceType.Disinfect, Value = 1f } } } } } }; public int LevelLayoutID { get; set; } public string DebugName { get; set; } = string.Empty; public List<LevelData> DataChanges { get; set; } = new List<LevelData>(); } public enum ResourceType { None, Standard, Special, Ammo, Tool, Health, Disinfect } public class ToolAbilityData { public class LevelData { public int Level { get; set; } public bool SentryRecall { get; set; } public float SentryRecallSpeed { get; set; } = 1f; public bool MoveMines { get; set; } public bool MineRecall { get; set; } public float MineRecallSpeed { get; set; } = 1f; } public static readonly ToolAbilityData[] Template = new ToolAbilityData[1] { new ToolAbilityData { DebugName = "Template", DataChanges = new List<LevelData> { new LevelData() } } }; public int LevelLayoutID { get; set; } public string DebugName { get; set; } = string.Empty; public List<LevelData> DataChanges { get; set; } = new List<LevelData>(); } } namespace XpExpansions { [BepInPlugin("Endskill.XpExpansions", "XpExpansions", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class BepinExLoader : BasePlugin { public const string MODNAME = "XpExpansions"; public const string AUTHOR = "Endskill"; public const string GUID = "Endskill.XpExpansions"; public const string VERSION = "1.1.0"; public override void Load() { LogManager.SetLogger(((BasePlugin)this).Log); LogManager._debugMessagesActive = ((BasePlugin)this).Config.Bind<bool>("Dev Settings", "DebugMessages", false, "This settings activates/deactivates debug messages in the console for this specific plugin.").Value; ClassInjector.RegisterTypeInIl2Cpp<ClientSidedBioTrackerAbility>(); CacheApi.SaveInstance<ExpansionManager>(new ExpansionManager(), CacheApiWrapper.ExtensionCacheName); } } } namespace XpExpansions.Util { public static class DoorUtil { private const float PortalCheckBuffer = 0.25f; private const float MinDotInNode = 0.1f; private static readonly List<AIG_CoursePortal> _portalCache = new List<AIG_CoursePortal>(); public static bool TryGetNearbyPortal(Vector3 pos, AIG_CourseNode node, [MaybeNullWhen(false)] out AIG_CoursePortal portal) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //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) float num = float.MaxValue; portal = null; Enumerator<AIG_CoursePortal> enumerator = node.m_portals.GetEnumerator(); float num2 = default(float); float num3 = default(float); while (enumerator.MoveNext()) { AIG_CoursePortal current = enumerator.Current; Vector3 val = pos - current.Position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; current.m_cullPortal.GetShapeWidthHeight(ref num2, ref num3); num2 /= 2f; num3 /= 2f; num2 += 0.25f; num3 += 0.25f; if (sqrMagnitude < num && sqrMagnitude < num2 * num2 + num3 * num3) { portal = current; num = sqrMagnitude; } } return portal != null; } public static bool TryGetCrossPortal(Vector3 pos, AIG_CourseNode nodeA, AIG_CourseNode nodeB, [MaybeNullWhen(false)] out AIG_CoursePortal portal) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) int nodeID = nodeB.NodeID; Enumerator<AIG_CoursePortal> enumerator = nodeA.m_portals.GetEnumerator(); while (enumerator.MoveNext()) { AIG_CoursePortal current = enumerator.Current; if (current.GetOppositeNode(nodeA).NodeID == nodeID) { _portalCache.Add(current); } } if (_portalCache.Count == 0) { portal = null; return false; } if (_portalCache.Count == 1) { portal = _portalCache[0]; _portalCache.Clear(); return true; } float num = float.MaxValue; portal = null; foreach (AIG_CoursePortal item in _portalCache) { Vector3 val = pos - item.Position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < num) { portal = item; num = sqrMagnitude; } } _portalCache.Clear(); return true; } public static bool IsOnSameSide(Vector3 pos, AIG_CourseNode checkNode, AIG_CoursePortal portal) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) C_Portal cullPortal = portal.m_cullPortal; Vector3 val = pos - cullPortal.m_center; return Vector3.Dot(cullPortal.GetDirIntoNode(checkNode.m_cullNode), val) > 0.1f; } public static AIG_CourseNode GetCorrectNode(Vector3 pos, AIG_CourseNode node) { //IL_0000: 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 (TryGetNearbyPortal(pos, node, out AIG_CoursePortal portal)) { AIG_CourseNode oppositeNode = portal.GetOppositeNode(node); if (IsOnSameSide(pos, oppositeNode, portal)) { return oppositeNode; } } return node; } public static bool CanTouchNode(Vector3 pos, AIG_CourseNode node, AIG_CourseNode checkNode) { //IL_0010: 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) if (node.NodeID == checkNode.NodeID) { return true; } if (!TryGetCrossPortal(pos, node, checkNode, out AIG_CoursePortal portal)) { return false; } if (portal.IsTraversable) { return true; } return IsOnSameSide(pos, checkNode, portal); } public static bool CanTouchNode(Vector3 pos, AIG_CourseNode checkNode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (!TryGetNearbyPortal(pos, checkNode, out AIG_CoursePortal portal) || portal.IsTraversable) { return true; } return IsOnSameSide(pos, checkNode, portal); } } } namespace XpExpansions.Scripts { public class ClientSidedBioTrackerAbility : MonoBehaviour { private float _nextUpdate; public ClientSidedBioTrackerAbility(IntPtr intPtr) : base(intPtr) { } public void Update() { if (!(Time.time > _nextUpdate)) { return; } if (GameStateManager.IsInExpedition) { PlayerManager.GetLocalPlayerAgent(); Enumerator<EnemyAgent> enumerator = AIG_CourseGraph.GetReachableEnemiesInNodes(((Agent)PlayerManager.GetLocalPlayerAgent()).CourseNode, 1).GetEnumerator(); while (enumerator.MoveNext()) { ToolSyncManager.WantToTagEnemy(enumerator.Current); } } _nextUpdate = Time.time + 10f; } } } namespace XpExpansions.Patches { [HarmonyBefore(new string[] { "Endskill.XpExpansions", "Endskill.GTFuckingXP" })] [HarmonyPatch(typeof(Dam_EnemyDamageBase))] public class EnemyDamageBasePatches { [HarmonyPatch("MeleeDamage")] [HarmonyPrefix] public static bool MeleePrefix(Dam_EnemyDamageBase __instance, ref float dam, Agent sourceAgent) { return true; } } } namespace XpExpansions.Manager { public abstract class BaseManager { private static string? _folderPath; protected string FolderPath { get { if (string.IsNullOrEmpty(_folderPath)) { _folderPath = CacheApiWrapper.GetFolderPath(); } return _folderPath; } } public virtual bool LocalOnly => true; public virtual void LevelReached(PlayerAgent player, Level level) { } public virtual void LevelInitialized(Level level) { } public virtual void Initialize() { } public virtual void LevelCleanup() { } } public class ClientSidedBioTrackerManager : BaseManager { private const string _expansionFileName = "BioTracker.json"; public ClientSidedBioTrackerManager() { LevelApi.AddEndLevelCallback((Action)DestroyBioTrackerAbility); } public override void Initialize() { WriteDefaultJsonBlocks(); UpdateEverything(); } public override void LevelReached(PlayerAgent player, Level level) { LevelLayout levelLayout = CacheApiWrapper.GetCurrentLevelLayout(); LocalBioTrackerData localBioTrackerData = CacheApi.GetInstance<List<LocalBioTrackerData>>(CacheApiWrapper.ExtensionCacheName).FirstOrDefault((LocalBioTrackerData it) => it.LevelLayoutPersistentId == levelLayout.PersistentId); if (localBioTrackerData != null) { if (localBioTrackerData.UnlockAtLevel <= level.LevelNumber) { CacheApiWrapper.DestroyOldCreateRegisterAndReturnComponent<ClientSidedBioTrackerAbility>(); } else { DestroyBioTrackerAbility(); } } } public void DestroyBioTrackerAbility() { CacheApiWrapper.KillScript<ClientSidedBioTrackerAbility>(); } private void WriteDefaultJsonBlocks() { if (!Directory.Exists(base.FolderPath)) { Directory.CreateDirectory(base.FolderPath); } string path = Path.Combine(base.FolderPath, "BioTracker.json"); if (!File.Exists(path)) { File.WriteAllText(path, ExpJson.Serialize(GetDefaultData())); } } private void UpdateEverything() { CacheApi.SaveInstance<List<LocalBioTrackerData>>(ExpJson.Deserialize<List<LocalBioTrackerData>>(File.ReadAllText(Path.Combine(base.FolderPath, "BioTracker.json"))), CacheApiWrapper.ExtensionCacheName); } private List<LocalBioTrackerData> GetDefaultData() { List<LocalBioTrackerData> list = new List<LocalBioTrackerData>(); for (int i = 0; i < 20; i++) { list.Add(new LocalBioTrackerData(i, 3)); } return list; } } public class DeactivateManager : BaseManager { private const string _expansionFileName = "DeactivateOnMissions.json"; private const string _expansionClassFileName = "DeactivateClassLayout.json"; public override void Initialize() { } private void WriteDefaultJsonBlocks() { if (base.FolderPath.Contains("BepInEx")) { if (!Directory.Exists(base.FolderPath)) { Directory.CreateDirectory(base.FolderPath); } string path = Path.Combine(base.FolderPath, "DeactivateOnMissions.json"); if (!File.Exists(path)) { File.WriteAllText(path, ExpJson.Serialize(new List<int> { 69420, 42069 })); } } } } public class DoubleJumpManager : BaseManager { public const string OldGUID = "com.mccad00.DoubleJump"; public const string DoubleJumpXpExpansionId = "Endskill.DoubleJumpExpansion"; private const string _expansionFileName = "DoubleJumpExpansion.json"; private const string _configFileName = "DoubleJumpDefaultConfig.json"; private static DoubleJumpConfig _baseConfig = new DoubleJumpConfig(); public static DoubleJumpConfig ActiveConfig { get; private set; } = new DoubleJumpConfig(); public static bool DoubleJumpUnlocked { get; private set; } public DoubleJumpManager() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) Harmony.UnpatchID("com.mccad00.DoubleJump"); DoubleJumpUnlocked = false; Harmony val = new Harmony("Endskill.DoubleJumpExpansion"); val.PatchAll(typeof(PLOC_Patches)); val.PatchAll(typeof(FallDamagePatches)); val.PatchAll(typeof(SuperJumpPatches)); } public override void Initialize() { WriteDefaultJsonBlocks(); UpdateEverything(); } public override void LevelCleanup() { DoubleJumpUnlocked = false; } public override void LevelReached(PlayerAgent player, Level level) { LogManager.Message("LevelReached, DoubleJump " + ((object)level).ToString()); LevelLayout levelLayout = CacheApiWrapper.GetCurrentLevelLayout(); DoubleJumpData doubleJumpData = CacheApi.GetInstance<List<DoubleJumpData>>(CacheApiWrapper.ExtensionCacheName).FirstOrDefault((DoubleJumpData it) => it.LevelLayoutPersistentId == levelLayout.PersistentId); if (doubleJumpData != null) { if (doubleJumpData.UnlockAtLevel <= level.LevelNumber) { DoubleJumpUnlocked = true; ActiveConfig = doubleJumpData.DoubleJumpConfig ?? _baseConfig; } else { DoubleJumpUnlocked = false; } } else { DoubleJumpUnlocked = false; } } private void WriteDefaultJsonBlocks() { if (!Directory.Exists(base.FolderPath)) { Directory.CreateDirectory(base.FolderPath); } string path = Path.Combine(base.FolderPath, "DoubleJumpExpansion.json"); if (!File.Exists(path)) { File.WriteAllText(path, "[\r\n {\r\n \"LevelLayoutPersistentId\": 13,\r\n \"UnlockAtLevel\": 10,\r\n \"DoubleJumpConfig\": null\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 14,\r\n \"UnlockAtLevel\": 6,\r\n \"DoubleJumpConfig\": null\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 7,\r\n \"UnlockAtLevel\": 5,\r\n \"DoubleJumpConfig\": null\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 8,\r\n \"UnlockAtLevel\": 5,\r\n \"DoubleJumpConfig\": null\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 15,\r\n \"UnlockAtLevel\": 5,\r\n \"DoubleJumpConfig\": null\r\n }\r\n]"); } string path2 = Path.Combine(base.FolderPath, "DoubleJumpDefaultConfig.json"); if (File.Exists(path2)) { _baseConfig = ExpJson.Deserialize<DoubleJumpConfig>(File.ReadAllText(path2)); return; } string path3 = Path.Combine(ConfigManager.CustomPath, "mccad00", "DoubleJump.json"); if (File.Exists(path3)) { _baseConfig = ExpJson.Deserialize<DoubleJumpConfig>(File.ReadAllText(path3)); } else { _baseConfig = new DoubleJumpConfig(); } File.WriteAllText(path2, ExpJson.Serialize(_baseConfig)); } private void UpdateEverything() { CacheApi.SaveInstance<List<DoubleJumpData>>(ExpJson.Deserialize<List<DoubleJumpData>>(File.ReadAllText(Path.Combine(base.FolderPath, "DoubleJumpExpansion.json"))), CacheApiWrapper.ExtensionCacheName); } } public class ExpansionManager : BaseManager { public const string ExpansionActivePath = "ExpansionsActive.json"; public override bool LocalOnly => false; public ExpansionManager() { LogManager.Message("ExpansionManager constructor."); InitApi.AddInitCallback((Action)Initialize); LevelApi.AddEndLevelCallback((Action)LevelCleanup); XpApi.AddOnLevelUpCallback((Action<PlayerAgent, Level>)LevelReached); XpApi.AddScriptsLoaded((Action<Level>)LevelInitialized); } public override void Initialize() { CacheApiWrapper.SetFolderPath(Path.Combine(ScriptManager.Instance.GetFolderPath(), "Expansions")); WriteDefaultData(); List<BaseManager> list = CreateManagers(); CacheApi.SaveInstance<List<BaseManager>>(list, CacheApiWrapper.ExtensionCacheName); foreach (BaseManager item in list) { item.Initialize(); } } public override void LevelCleanup() { foreach (BaseManager item in CacheApi.GetInstance<List<BaseManager>>(CacheApiWrapper.ExtensionCacheName)) { item.LevelCleanup(); } } public override void LevelReached(PlayerAgent player, Level level) { LogManager.Message("LevelReached in the Expansion Manager."); foreach (BaseManager item in CacheApi.GetInstance<List<BaseManager>>(CacheApiWrapper.ExtensionCacheName)) { if (!item.LocalOnly || ((Agent)player).IsLocallyOwned) { item.LevelReached(player, level); } } } public override void LevelInitialized(Level level) { LogManager.Message("LevelInitialized in the Expansion Manager."); foreach (BaseManager item in CacheApi.GetInstance<List<BaseManager>>(CacheApiWrapper.ExtensionCacheName)) { item.LevelInitialized(level); } } private List<BaseManager> CreateManagers() { List<BaseManager> list = new List<BaseManager>(); ActiveExpansions activeExpansions = ExpJson.Deserialize<ActiveExpansions>(File.ReadAllText(Path.Combine(base.FolderPath, "ExpansionsActive.json"))); LogManager.Debug("CreateManagers method."); if (activeExpansions != null) { if (activeExpansions.DoubleJumpAbility) { list.Add(new DoubleJumpManager()); } if (activeExpansions.StartingXp) { list.Add(new StartingLevelXpManager()); } if (activeExpansions.LivingBioAbility) { list.Add(new ClientSidedBioTrackerManager()); } if (activeExpansions.PackConverter) { list.Add(new PackConverterManager()); } if (activeExpansions.ToolAbility) { list.Add(new ToolAbilityManager()); } } return list; } private void WriteDefaultData() { if (!Directory.Exists(base.FolderPath)) { Directory.CreateDirectory(base.FolderPath); } string path = Path.Combine(base.FolderPath, "ExpansionsActive.json"); if (!File.Exists(path)) { File.WriteAllText(path, ExpJson.Serialize(new ActiveExpansions())); } } } public class ExplosionAbilityManager : BaseManager { public const string ExplosionHarmonyInstanceId = "Endskill.ExplosionAbility"; public Harmony ExplosionHarmony { get; private set; } public ExplosionAbilityManager() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown ExplosionHarmony = new Harmony("Endskill.ExplosionAbility"); } public override void Initialize() { } public override void LevelCleanup() { } } public static class LogManager { private static ManualLogSource logger; internal static bool _debugMessagesActive; internal static void SetLogger(ManualLogSource log) { logger = log; } public static void Verbose(object msg) { if (_debugMessagesActive) { logger.LogInfo(msg); } } public static void Debug(object msg) { if (_debugMessagesActive) { logger.LogDebug(msg); } } public static void Message(object msg) { if (_debugMessagesActive) { logger.LogMessage(msg); } } public static void Error(object msg) { logger.LogError(msg); } public static void Warn(object msg) { logger.LogWarning(msg); } } public class PackConverterManager : BaseManager { private class ConversionWrapper { private readonly Dictionary<ResourceType, List<PackConverterData.LevelData.ConversionData>> _typeToConversions = new Dictionary<ResourceType, List<PackConverterData.LevelData.ConversionData>>(); public ConversionWrapper(PackConverterData.LevelData dataChanges) { foreach (PackConverterData.LevelData.ConversionData conversion in dataChanges.Conversions) { if (!_typeToConversions.TryGetValue(conversion.ReceiveType, out List<PackConverterData.LevelData.ConversionData> value)) { value = (_typeToConversions[conversion.ReceiveType] = new List<PackConverterData.LevelData.ConversionData>()); } value.Add(conversion); } } public void ApplyConversions(PlayerAgent player, ResourceType type, float amount) { if (_typeToConversions.TryGetValue(type, out List<PackConverterData.LevelData.ConversionData> value)) { float standard = 0f; float special = 0f; float tool = 0f; float disinfect = 0f; float health = 0f; AddConversions(value, amount, ref standard, ref special, ref tool, ref health, ref disinfect); ApplyConversions(player, standard, special, tool, health, disinfect); } } public void ApplyConversions(PlayerAgent player, float standardAmount, float specialAmount, float toolAmount) { float standard = 0f; float special = 0f; float tool = 0f; float disinfect = 0f; float health = 0f; if (_typeToConversions.TryGetValue(ResourceType.Standard, out List<PackConverterData.LevelData.ConversionData> value)) { AddConversions(value, standardAmount, ref standard, ref special, ref tool, ref health, ref disinfect); } if (_typeToConversions.TryGetValue(ResourceType.Special, out value)) { AddConversions(value, specialAmount, ref standard, ref special, ref tool, ref health, ref disinfect); } if (_typeToConversions.TryGetValue(ResourceType.Ammo, out value)) { AddConversions(value, (standardAmount + specialAmount) / 2f, ref standard, ref special, ref tool, ref health, ref disinfect); } if (_typeToConversions.TryGetValue(ResourceType.Tool, out value)) { AddConversions(value, toolAmount, ref standard, ref special, ref tool, ref health, ref disinfect); } ApplyConversions(player, standard, special, tool, health, disinfect); } private static void AddConversions(List<PackConverterData.LevelData.ConversionData> list, float amount, ref float standard, ref float special, ref float tool, ref float health, ref float disinfect) { foreach (PackConverterData.LevelData.ConversionData item in list) { float num = item.Value * amount; switch (item.ConvertType) { case ResourceType.Standard: standard += num; break; case ResourceType.Special: special += num; break; case ResourceType.Ammo: standard += num / 2f; special += num / 2f; break; case ResourceType.Tool: tool += num; break; case ResourceType.Health: health += num; break; case ResourceType.Disinfect: disinfect += num; break; } } } private static void ApplyConversions(PlayerAgent player, float standard, float special, float tool, float health, float disinfect) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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) if (standard != 0f || special != 0f || tool != 0f) { PlayerBackpackManager.GiveAmmoToPlayer(player.Owner, standard, special, tool); } if (health != 0f) { PlayerBackpackManager.PickupHealthRel(health, player); } if (disinfect != 0f) { player.Damage.ModifyInfection(new pInfection { amount = 0f - disinfect, effect = (pInfectionEffect)1, mode = (pInfectionMode)1 }, true, true); } } } public const string ExpansionID = "Endskill.PackConverter"; private const string _expansionFileName = "PackConverterExpansion.json"; private static readonly Dictionary<IntPtr, ConversionWrapper> _activeConversions = new Dictionary<IntPtr, ConversionWrapper>(); private static List<PackConverterData> JsonData = new List<PackConverterData>(); public override bool LocalOnly => false; public static void ApplyConversions(PlayerAgent player, ResourceType type, float amount) { if (_activeConversions.TryGetValue(((Il2CppObjectBase)player).Pointer, out ConversionWrapper value)) { value.ApplyConversions(player, type, amount); } } public static void ApplyConversions(PlayerAgent player, float standardAmount, float specialAmount, float toolAmount) { if (_activeConversions.TryGetValue(((Il2CppObjectBase)player).Pointer, out ConversionWrapper value)) { value.ApplyConversions(player, standardAmount, specialAmount, toolAmount); } } public PackConverterManager() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) new Harmony("Endskill.PackConverter").PatchAll(typeof(ResourcePatches)); } public override void Initialize() { WriteDefaultJsonBlocks(); UpdateEverything(); } public override void LevelCleanup() { _activeConversions.Clear(); } public override void LevelReached(PlayerAgent player, Level level) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) ActiveLevel val = default(ActiveLevel); if (!CacheApiWrapper.TryGetFullActiveLevel(player, ref val)) { return; } int levelLayoutID = val.Layout.PersistentId; PackConverterData packConverterData = JsonData.Find((PackConverterData it) => it.LevelLayoutID == levelLayoutID); if (packConverterData == null) { return; } for (int num = packConverterData.DataChanges.Count - 1; num >= 0; num--) { PackConverterData.LevelData levelData = packConverterData.DataChanges[num]; if (levelData.Level <= level.LevelNumber) { _activeConversions[((Il2CppObjectBase)player).Pointer] = new ConversionWrapper(levelData); break; } } } private void WriteDefaultJsonBlocks() { if (!Directory.Exists(base.FolderPath)) { Directory.CreateDirectory(base.FolderPath); } string path = Path.Combine(base.FolderPath, "PackConverterExpansion.json"); if (!File.Exists(path)) { File.WriteAllText(path, ExpJson.Serialize(PackConverterData.Template)); } } private void UpdateEverything() { JsonData = ExpJson.Deserialize<List<PackConverterData>>(File.ReadAllText(Path.Combine(base.FolderPath, "PackConverterExpansion.json"))); foreach (PackConverterData jsonDatum in JsonData) { jsonDatum.DataChanges.Sort((PackConverterData.LevelData x, PackConverterData.LevelData y) => x.Level.CompareTo(y.Level)); } } } internal class StartingLevelXpManager : BaseManager { private const string _expansionFileName = "StartingXP.json"; public override void Initialize() { WriteDefaultJsonBlocks(); UpdateEverything(); } public override void LevelInitialized(Level level) { ExpeditionInTierData expedition = RundownManager.ActiveExpedition; StartingXpData startingXpData = CacheApi.GetInstance<List<StartingXpData>>(CacheApiWrapper.ExtensionCacheName).FirstOrDefault((StartingXpData it) => it.LevelLayoutData == expedition.LevelLayoutData); if (startingXpData != null) { XpApi.AddXp(startingXpData.StartingXp); } } private void UpdateEverything() { CacheApi.SaveInstance<List<StartingXpData>>(ExpJson.Deserialize<List<StartingXpData>>(File.ReadAllText(Path.Combine(base.FolderPath, "StartingXP.json"))), CacheApiWrapper.ExtensionCacheName); } private void WriteDefaultJsonBlocks() { if (!Directory.Exists(base.FolderPath)) { Directory.CreateDirectory(base.FolderPath); } string path = Path.Combine(base.FolderPath, "StartingXP.json"); if (!File.Exists(path)) { File.WriteAllText(path, ExpJson.Serialize(GetDefaultData())); } } private List<StartingXpData> GetDefaultData() { List<StartingXpData> list = new List<StartingXpData>(); foreach (RundownDataBlock allBlock in GameDataBlockBase<RundownDataBlock>.GetAllBlocks()) { Enumerator<ExpeditionInTierData> enumerator2 = allBlock.TierD.GetEnumerator(); while (enumerator2.MoveNext()) { ExpeditionInTierData current = enumerator2.Current; list.Add(new StartingXpData(current.LevelLayoutData, 100)); } } return list; } } public class ToolAbilityManager : BaseManager { private struct MoveMineData { public pReplicator mine; public pCourseNode courseNode; public Vector3 position; public Quaternion rotation; } public const string ExpansionID = "Endskill.ToolAbility"; private const string _expansionFileName = "ToolAbilityExpansion.json"; private const string PickupNetworkID = "ToolAbilityPickup"; private const string MoveMineNetworkID = "ToolAbilityMoveMine"; private static List<ToolAbilityData> JsonData = new List<ToolAbilityData>(); public static ToolAbilityData.LevelData ActiveAbility { get; private set; } = new ToolAbilityData.LevelData(); public ToolAbilityManager() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) Harmony val = new Harmony("Endskill.ToolAbility"); val.PatchAll(typeof(PlayerPatches)); val.PatchAll(typeof(SentryPatches)); val.PatchAll(typeof(MinePatches)); ClassInjector.RegisterTypeInIl2Cpp<ToolAbilityHandler>(); NetworkAPI.RegisterEvent<pReplicator>("ToolAbilityPickup", (Action<ulong, pReplicator>)PickupSentry); NetworkAPI.RegisterEvent<MoveMineData>("ToolAbilityMoveMine", (Action<ulong, MoveMineData>)MoveMine); } public override void Initialize() { WriteDefaultJsonBlocks(); UpdateEverything(); } public override void LevelCleanup() { ActiveAbility = new ToolAbilityData.LevelData(); } public override void LevelReached(PlayerAgent player, Level level) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) ActiveLevel val = default(ActiveLevel); if (!CacheApiWrapper.TryGetFullActiveLevel(player, ref val)) { return; } int levelLayoutID = val.Layout.PersistentId; ToolAbilityData toolAbilityData = JsonData.Find((ToolAbilityData it) => it.LevelLayoutID == levelLayoutID); if (toolAbilityData == null) { return; } for (int num = toolAbilityData.DataChanges.Count - 1; num >= 0; num--) { ToolAbilityData.LevelData levelData = toolAbilityData.DataChanges[num]; if (levelData.Level <= level.LevelNumber) { ActiveAbility = levelData; break; } } } private void WriteDefaultJsonBlocks() { if (!Directory.Exists(base.FolderPath)) { Directory.CreateDirectory(base.FolderPath); } string path = Path.Combine(base.FolderPath, "ToolAbilityExpansion.json"); if (!File.Exists(path)) { File.WriteAllText(path, ExpJson.Serialize(ToolAbilityData.Template)); } } private void UpdateEverything() { JsonData = ExpJson.Deserialize<List<ToolAbilityData>>(File.ReadAllText(Path.Combine(base.FolderPath, "ToolAbilityExpansion.json"))); foreach (ToolAbilityData jsonDatum in JsonData) { jsonDatum.DataChanges.Sort((ToolAbilityData.LevelData x, ToolAbilityData.LevelData y) => x.Level.CompareTo(y.Level)); } } public static void DoSentryPickup(SentryGunInstance sentry) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) pReplicator val = default(pReplicator); ((pReplicator)(ref val)).SetID(sentry.Replicator); NetworkAPI.InvokeEvent<pReplicator>("ToolAbilityPickup", val, SNet.Master, (SNet_ChannelType)2); } public static void DoMineMove(MineDeployerInstance mine, Vector3 newPos, Quaternion rotation) { //IL_001c: 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_0024: 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_0031: Unknown result type (might be due to invalid IL or missing references) MoveMineData moveMineData = default(MoveMineData); ((pReplicator)(ref moveMineData.mine)).SetID(mine.Replicator); moveMineData.position = newPos; moveMineData.rotation = rotation; ((pCourseNode)(ref moveMineData.courseNode)).Set(DoorUtil.GetCorrectNode(newPos, ((Agent)((Item)mine).Owner).CourseNode)); NetworkAPI.InvokeEvent<MoveMineData>("ToolAbilityMoveMine", moveMineData, (SNet_ChannelType)2); MoveMine(0uL, moveMineData); } private static void PickupSentry(ulong _, pReplicator packet) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) IReplicator val = default(IReplicator); if (!((pReplicator)(ref packet)).TryGetID(ref val) || val == null) { return; } SentryGunInstance val2 = ((Il2CppObjectBase)val.ReplicatorSupplier).Cast<SentryGunInstance>(); if (!val2.Alive || (Object)(object)((Item)val2).Owner == (Object)null) { return; } PlayerAgent owner = ((Item)val2).Owner; SNet_Player owner2 = owner.Owner; PlayerBackpack val3 = default(PlayerBackpack); BackpackItem val4 = default(BackpackItem); if (!PlayerBackpackManager.TryGetBackpack(owner2, ref val3) || !val3.TryGetBackpackItem((InventorySlot)3, ref val4)) { return; } if (SNet.IsMaster) { if (!val2.Alive) { return; } NetworkAPI.InvokeEvent<pReplicator>("ToolAbilityPickup", packet, (SNet_ChannelType)2); } if ((int)val4.Status != 0) { val4.Status = (eInventoryItemStatus)0; if (owner2.IsLocal || (SNet.IsMaster && owner2.IsBot)) { pInventoryItemStatus val5 = default(pInventoryItemStatus); ((pPlayer)(ref val5.sourcePlayer)).SetPlayer(owner2); val5.slot = (InventorySlot)3; val5.status = (eInventoryItemStatus)0; PlayerBackpackManager.InventoryItemStatusChange(val5, (SNet_Player)null); } else { val3.ShowHideInstance(val4, true); } GuiManager.PlayerLayer.Inventory.UpdateItemUI(val4, owner.Inventory.WieldedSlot, true); } ((Item)val2).SyncedPickup(owner); } private static void MoveMine(ulong _, MoveMineData data) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) IReplicator val = default(IReplicator); if (((pReplicator)(ref data.mine)).TryGetID(ref val) && val != null) { MineDeployerInstance val2 = ((Il2CppObjectBase)val.ReplicatorSupplier).Cast<MineDeployerInstance>(); Transform transform = ((Component)val2).transform; transform.position = data.position; transform.rotation = data.rotation; AIG_CourseNode courseNode = default(AIG_CourseNode); if (((pCourseNode)(ref data.courseNode)).TryGet(ref courseNode)) { val2.m_courseNode = courseNode; } ((ItemEquippable)val2).Sound.Post(EVENTS.STICKYMINEATTACH, data.position); } } } } namespace XpExpansions.Information { public class ActiveExpansions { public bool DoubleJumpAbility { get; set; } = true; public bool StartingXp { get; set; } public bool LivingBioAbility { get; set; } public bool PackConverter { get; set; } = true; public bool ToolAbility { get; set; } = true; } internal static class ExpJson { private static readonly JsonSerializerOptions _setting; static ExpJson() { _setting = new JsonSerializerOptions { ReadCommentHandling = JsonCommentHandling.Skip, IncludeFields = true, PropertyNameCaseInsensitive = true, WriteIndented = true, IgnoreReadOnlyProperties = true }; _setting.Converters.Add(new JsonStringEnumConverter()); } public static T? Deserialize<T>(string json) { return JsonSerializer.Deserialize<T>(json, _setting); } public static string Serialize<T>(T value) { return JsonSerializer.Serialize(value, _setting); } } } namespace XpExpansions.Information.StartingXp { public class StartingXpData { public uint LevelLayoutData { get; set; } public int StartingXp { get; set; } public StartingXpData(uint levelLayoutData, int startingXp) { LevelLayoutData = levelLayoutData; StartingXp = startingXp; } } } namespace XpExpansions.Information.Explosion { public class ExplosionAbilityData { public int LevelLayoutPersistentId { get; set; } public int UnlockAtLevel { get; set; } public ExplosionAbilityData(int levelLayoutPersistentId, int unlockAtLevel) { LevelLayoutPersistentId = levelLayoutPersistentId; UnlockAtLevel = unlockAtLevel; } } } namespace XpExpansions.Information.DoubleJump { public sealed class DoubleJumpConfig { public bool DoubleJumpEnabled { get; set; } = true; public bool SuperJumpEnabled { get; set; } = true; public float SuperJumpVelocity { get; set; } = 3f; } internal class DoubleJumpData { public int LevelLayoutPersistentId { get; set; } public int UnlockAtLevel { get; set; } public DoubleJumpConfig? DoubleJumpConfig { get; set; } public DoubleJumpData(int levelLayoutPersistentId, int unlockAtLevel, DoubleJumpConfig? doubleJumpConfig) { LevelLayoutPersistentId = levelLayoutPersistentId; UnlockAtLevel = unlockAtLevel; DoubleJumpConfig = doubleJumpConfig; } } } namespace XpExpansions.Information.BioTrackerLocal { internal class LocalBioTrackerData { public int LevelLayoutPersistentId { get; set; } public int UnlockAtLevel { get; set; } public LocalBioTrackerData(int levelLayoutPersistentId, int unlockAtLevel) { LevelLayoutPersistentId = levelLayoutPersistentId; UnlockAtLevel = unlockAtLevel; } } } namespace XpExpansions.Extensions { public static class CacheApiWrapper { internal static string ExtensionCacheName = "XpExtensions"; private const string _folderPath = "FolderPath"; public static void SetFolderPath(string path) { CacheApi.SaveInformation((object)"FolderPath", (object)path, ExtensionCacheName); } public static string GetFolderPath() { return CacheApi.GetInformation<string>((object)"FolderPath", ExtensionCacheName); } } }
GTFuckingXP.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using Agents; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP.Utils.Collections; using CellMenu; using EndskApi.Api; using EndskApi.Enums.Menus; using EndskApi.Information.EnemyKill; using EndskApi.Information.Menus; using EndskApi.Manager; using EndskApi.Scripts; using Enemies; using FloatingTextAPI; using GTFO.API; using GTFuckingXP.Communication; using GTFuckingXP.Dependencies; using GTFuckingXP.Enums; using GTFuckingXP.Extensions; using GTFuckingXP.Extensions.Information; using GTFuckingXP.Extensions.Information.Level.Json; using GTFuckingXP.Information; using GTFuckingXP.Information.ClassSelector; using GTFuckingXP.Information.Enemies; using GTFuckingXP.Information.Level; using GTFuckingXP.Information.NetworkingInfo; using GTFuckingXP.Managers; using GTFuckingXP.Patches; using GTFuckingXP.Patches.SelectLevelPatches; using GTFuckingXP.Scripts; using GTFuckingXp.Managers; using GTFuckingXp.Patches; using GameData; using Gear; using HarmonyLib; using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Localization; using MTFO.API; using MTFO.Ext.PartialData; using Microsoft.CodeAnalysis; using ModifierAPI; using Player; using SNetwork; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("GTFuckingXP")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+git33620c7-dirty-master.33620c7e9766136dea115db87926bb0ec5e6969c")] [assembly: AssemblyProduct("GTFuckingXP")] [assembly: AssemblyTitle("GTFuckingXP")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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; } } } namespace GTFuckingXp.Patches { [HarmonyPatch(typeof(Dam_EnemyDamageLimb))] public static class EnemyDamageLimbPatches { [HarmonyPatch("MeleeDamage")] [HarmonyPrefix] public static void MeleePrefix(Dam_EnemyDamageLimb __instance, ref float dam, Agent sourceAgent) { if (((Agent)__instance.m_base.Owner).Alive && (Object)(object)sourceAgent != (Object)null && sourceAgent.IsLocallyOwned) { float num = dam; LogManager.Debug($"Melee damage from local player registered. {num} was scaled up to:"); num *= CacheApiWrapper.GetActiveLevel().MeleeDamageMultiplier; LogManager.Debug($"{num}"); dam = num; } } [HarmonyPatch("BulletDamage")] [HarmonyPrefix] public static void BulletPostfix(Dam_EnemyDamageLimb __instance, ref float dam, Agent sourceAgent) { if (((Agent)__instance.m_base.Owner).Alive && (Object)(object)sourceAgent != (Object)null && sourceAgent.IsLocallyOwned && !SentryGunCheckPatches.SentryShot) { float num = dam; LogManager.Debug($"Bullet damage from local player registered. {num} was scaled to:"); num *= CacheApiWrapper.GetActiveLevel().WeaponDamageMultiplier; LogManager.Debug($"{num}"); dam = num; } } } [HarmonyPatch] internal static class PlayerDamagePatches { [HarmonyPatch(typeof(Dam_PlayerDamageLimb), "BulletDamage")] [HarmonyPrefix] private static void Prefix_BulletDamage(Dam_PlayerDamageLimb __instance, ref float dam, Agent sourceAgent) { PlayerAgent owner = __instance.m_base.Owner; if (((Agent)owner).Alive) { if (CacheApiWrapper.TryGetActiveLevel(owner, out Level level) && level.CustomScaling.TryGetValue(CustomScaling.BulletResistance, out var value)) { dam *= 2f - value; } if (!SentryGunCheckPatches.SentryShot && (Object)(object)sourceAgent != (Object)null && sourceAgent.IsLocallyOwned) { float num = dam; LogManager.Debug($"Bullet damage from local player registered. {num} was scaled up to:"); num *= CacheApiWrapper.GetActiveLevel().WeaponDamageMultiplier; LogManager.Debug($"{num}"); dam = num; } } } [HarmonyPatch(typeof(Dam_SyncedDamageBase), "FireDamage")] [HarmonyPrefix] private static void Prefix_FireDamage(Dam_SyncedDamageBase __instance, ref float dam) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)__instance.DamageBaseOwner == 0) { PlayerAgent val = ((Il2CppObjectBase)__instance.GetBaseAgent()).Cast<PlayerAgent>(); if (((Agent)val).Alive && ((Agent)val).IsLocallyOwned && CacheApiWrapper.GetActiveLevel().CustomScaling.TryGetValue(CustomScaling.BleedResistance, out var value)) { dam *= 2f - value; } } } [HarmonyPatch(typeof(Dam_SyncedDamageBase), "ExplosionDamage")] [HarmonyPrefix] private static void Prefix_ExplosionDamage(Dam_SyncedDamageBase __instance, ref float dam) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)__instance.DamageBaseOwner == 0 && CacheApiWrapper.TryGetActiveLevel(((Il2CppObjectBase)__instance.GetBaseAgent()).Cast<PlayerAgent>(), out Level level) && level.CustomScaling.TryGetValue(CustomScaling.ExplosionResistance, out var value)) { dam *= 2f - value; } } } [HarmonyPatch(typeof(Dam_PlayerDamageBase))] internal static class PlayerRegenPatches { [HarmonyPatch("OnIncomingDamage")] [HarmonyPostfix] private static void Postfix_OnDamage(Dam_PlayerDamageBase __instance) { PlayerAgent owner = __instance.Owner; if (CacheApiWrapper.TryGetActiveLevel(owner, out Level level) && level.CustomScaling.TryGetValue(CustomScaling.RegenStartDelayMultiplier, out var value)) { __instance.m_nextRegen = Clock.Time + owner.PlayerData.healthRegenStartDelayAfterDamage * value; } } } [HarmonyPatch(typeof(BulletWeapon))] internal static class SentryGunCheckPatches { internal static bool SentryShot { get; private set; } [HarmonyPatch("BulletHit")] [HarmonyBefore(new string[] { "Dinorush.ExtraWeaponCustomization" })] [HarmonyPrefix] private static void Prefix_BulletHit(ref WeaponHitData weaponRayData, bool allowDirectionalBonus) { if (!allowDirectionalBonus || weaponRayData.vfxBulletHit != null) { SentryShot = true; } } [HarmonyPatch("BulletHit")] [HarmonyPostfix] private static void Postfix_BulletHit() { SentryShot = false; } } } namespace GTFuckingXp.Managers { public static class EnemyKillManager { public static void Setup() { EnemyKillApi.AddEnemyKilledCallback((Action<EnemyKillDistribution>)EnemyKilled); } public static void EnemyKilled(EnemyKillDistribution info) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)info.LastHitDealtBy == (Object)null) { GiveXpToEveryone(info.KilledEnemyAgent); return; } EnemyXp enemyXp = GetEnemyXp(info.KilledEnemyAgent); Vector3 position = info.KilledEnemyAgent.Position; position.y += 1f; Enumerator<SNet_Player> enumerator = SNet.SessionHub.PlayersInSession.GetEnumerator(); XpHandler xpHandler = default(XpHandler); while (enumerator.MoveNext()) { SNet_Player current = enumerator.Current; if (!((Object)(object)current != (Object)null) || current.IsBot) { continue; } if (info.LastHitDealtBy.Owner.PlayerSlot.index == current.PlayerSlot.index) { if (current.IsLocal) { if (CacheApi.TryGetInstance<XpHandler>(ref xpHandler, "GTF_XP", true)) { PlayerReviveManager.AddXP(current.Lookup, ((IXpData)enemyXp).GetXp(isDebuff: false)); xpHandler.AddXp(enemyXp, position); } } else { NetworkApiXpManager.SendReceiveXp(current, enemyXp, position, forceDebuffXp: false); } continue; } float damageDealtBySnet = info.GetDamageDealtBySnet(current); float healthMax = ((Dam_SyncedDamageBase)info.KilledEnemyAgent.Damage).HealthMax; float num = damageDealtBySnet / healthMax; bool flag = info.DidSnetBiotag(current); if (flag) { float biotagXpFrac = enemyXp.BiotagXpFrac; if (biotagXpFrac < 0f) { biotagXpFrac = CacheApiWrapper.GetGlobalValues().BiotagXpFrac; } num = Math.Max(num, biotagXpFrac); flag = biotagXpFrac > 0f; } if (damageDealtBySnet > 0.5f || flag) { LogManager.Debug($"percentageDealt = {num}, damageDealt = {damageDealtBySnet}"); NetworkApiXpManager.SendStaticXpInfo(current, (int)((float)enemyXp.XpGain * num), (int)((float)enemyXp.DebuffXp * num), (int)((float)enemyXp.LevelScalingXpDecrese * num), position); } } } private static void GiveXpToEveryone(EnemyAgent killedEnemy) { //IL_0008: 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) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) EnemyXp enemyXp = GetEnemyXp(killedEnemy); Vector3 position = killedEnemy.Position; position.y += 1f; XpHandler xpHandler = default(XpHandler); if (CacheApi.TryGetInstance<XpHandler>(ref xpHandler, "GTF_XP", true)) { xpHandler.AddXp(enemyXp, position, forceDebuffXp: true, "<#F30>"); } NetworkApiXpManager.SendHalfAssedXp(enemyXp, position, forceDebuffXp: true); } private static EnemyXp GetEnemyXp(EnemyAgent killedEnemy) { List<EnemyXp> instance = CacheApi.GetInstance<List<EnemyXp>>("GTF_XP"); EnemyXp enemyXp = instance.FirstOrDefault((EnemyXp it) => it.EnemyId == killedEnemy.EnemyDataID); if (enemyXp == null) { LogManager.Warn($"There was no enemy XP data found for {killedEnemy.EnemyDataID}!"); enemyXp = new EnemyXp(killedEnemy.EnemyDataID, ((Object)killedEnemy).name, 0, 0, 0); instance.Add(enemyXp); CacheApi.SaveInstance<List<EnemyXp>>(instance, "GTF_XP"); } LogManager.Debug($"Enemy kill was registered. Enemy XpData was {enemyXp.XpGain}."); return enemyXp; } } } namespace GTFuckingXp.Information { public class DefaultConstants { public const string DoubleJumpExpansion = "[\r\n {\r\n \"LevelLayoutPersistentId\": 13,\r\n \"UnlockAtLevel\": 10,\r\n \"DoubleJumpConfig\": null\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 14,\r\n \"UnlockAtLevel\": 6,\r\n \"DoubleJumpConfig\": null\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 7,\r\n \"UnlockAtLevel\": 5,\r\n \"DoubleJumpConfig\": null\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 8,\r\n \"UnlockAtLevel\": 5,\r\n \"DoubleJumpConfig\": null\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 15,\r\n \"UnlockAtLevel\": 5,\r\n \"DoubleJumpConfig\": null\r\n }\r\n]"; public const string ExpansionsActive = "\r\n{\r\n \"ExplosionAbility\": false,\r\n \"DoubleJumpAbility\": true\r\n}\r\n"; public const string BoosterEffects = "\r\n[\r\n {\r\n \"ClassLayoutPersistentId\": 2,\r\n \"ActiveLevels\": [\r\n 10\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 1.35\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.03,\r\n \"ProjectileResistance\": 1.03,\r\n \"InfectionResistance\": 1.03\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 2\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.06,\r\n \"ProjectileResistance\": 1.06,\r\n \"InfectionResistance\": 1.06\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.09,\r\n \"ProjectileResistance\": 1.09,\r\n \"InfectionResistance\": 1.09\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.12,\r\n \"ProjectileResistance\": 1.12,\r\n \"InfectionResistance\": 1.12\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.15,\r\n \"ProjectileResistance\": 1.15,\r\n \"InfectionResistance\": 1.15\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 6\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.18,\r\n \"ProjectileResistance\": 1.18,\r\n \"InfectionResistance\": 1.18\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 7\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.21,\r\n \"ProjectileResistance\": 1.21,\r\n \"InfectionResistance\": 1.21\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 8\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.24,\r\n \"ProjectileResistance\": 1.24,\r\n \"InfectionResistance\": 1.24\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 9\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.27,\r\n \"ProjectileResistance\": 1.27,\r\n \"InfectionResistance\": 1.27\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 10\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.3,\r\n \"ProjectileResistance\": 1.3,\r\n \"InfectionResistance\": 1.3\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 11\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.33,\r\n \"ProjectileResistance\": 1.33,\r\n \"InfectionResistance\": 1.33\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 12\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.36,\r\n \"ProjectileResistance\": 1.36,\r\n \"InfectionResistance\": 1.36\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 13\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.39,\r\n \"ProjectileResistance\": 1.39,\r\n \"InfectionResistance\": 1.39\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 14\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.42,\r\n \"ProjectileResistance\": 1.42,\r\n \"InfectionResistance\": 1.42\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 3,\r\n \"ActiveLevels\": [\r\n 15\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.45,\r\n \"ProjectileResistance\": 1.45,\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 4,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 1.35,\r\n \"ReviveSpeedSupport\": 1.25\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 4,\r\n \"ActiveLevels\": [\r\n 2\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 1.7,\r\n \"ReviveSpeedSupport\": 1.5\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 4,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 2.05,\r\n \"ReviveSpeedSupport\": 1.75\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 4,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 2.40,\r\n \"ReviveSpeedSupport\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 4,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 2.75,\r\n \"ReviveSpeedSupport\": 2.25,\r\n \"MeleeResistance\": 1.15,\r\n \"ProjectileResistance\": 1.15\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 15,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 12,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 1.35,\r\n \"ReviveSpeedSupport\": 1.1,\r\n \"MeleeResistance\": 1.04,\r\n \"ProjectileResistance\": 1.04,\r\n \"InfectionResistance\": 1.05\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 12,\r\n \"ActiveLevels\": [\r\n 2\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 1.7,\r\n \"ReviveSpeedSupport\": 1.2,\r\n \"MeleeResistance\": 1.08,\r\n \"ProjectileResistance\": 1.08,\r\n \"InfectionResistance\": 1.1\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 12,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 2.05,\r\n \"ReviveSpeedSupport\": 1.3,\r\n \"MeleeResistance\": 1.12,\r\n \"ProjectileResistance\": 1.12,\r\n \"InfectionResistance\": 1.15\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 12,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 2.40,\r\n \"ReviveSpeedSupport\": 1.4,\r\n \"MeleeResistance\": 1.16,\r\n \"ProjectileResistance\": 1.16,\r\n \"InfectionResistance\": 1.2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 12,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 2.75,\r\n \"ReviveSpeedSupport\": 1.5,\r\n \"MeleeResistance\": 1.2,\r\n \"ProjectileResistance\": 1.2,\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 5,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 1.55,\r\n \"ScannerRechargeSpeed\": 1.15,\r\n \"SentryGunDamage\": 1.15,\r\n \"GlueEfficiency\": 1.15,\r\n \"TripMineDamage\": 1.15,\r\n \"ComputerProcessingSpeed\": 1.2,\r\n \"FogRepellerEffect\": 1.2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 5,\r\n \"ActiveLevels\": [\r\n 2\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 2.1,\r\n \"ScannerRechargeSpeed\": 1.3,\r\n \"SentryGunDamage\": 1.3,\r\n \"GlueEfficiency\": 1.3,\r\n \"TripMineDamage\": 1.3,\r\n \"ComputerProcessingSpeed\": 1.4,\r\n \"FogRepellerEffect\": 1.4\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 5,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 2.65,\r\n \"ScannerRechargeSpeed\": 1.45,\r\n \"SentryGunDamage\": 1.45,\r\n \"GlueEfficiency\": 1.45,\r\n \"TripMineDamage\": 1.45,\r\n \"ComputerProcessingSpeed\": 1.6,\r\n \"FogRepellerEffect\": 1.6\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 5,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 3.2,\r\n \"ScannerRechargeSpeed\": 1.6,\r\n \"SentryGunDamage\": 1.6,\r\n \"GlueEfficiency\": 1.6,\r\n \"TripMineDamage\": 1.6,\r\n \"ComputerProcessingSpeed\": 1.8,\r\n \"FogRepellerEffect\": 1.8\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 5,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 999.0,\r\n \"ScannerRechargeSpeed\": 1.8,\r\n \"SentryGunDamage\": 1.75,\r\n \"GlueEfficiency\": 1.75,\r\n \"TripMineDamage\": 1.75,\r\n \"ComputerProcessingSpeed\": 999.0,\r\n \"FogRepellerEffect\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 6,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.5,\r\n \"ProjectileResistance\": 0.5\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 6,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 1.5,\r\n \"MeleeResistance\": 0.46,\r\n \"ProjectileResistance\": 0.46\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 6,\r\n \"ActiveLevels\": [\r\n 2\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 2.0,\r\n \"MeleeResistance\": 0.42,\r\n \"ProjectileResistance\": 0.42\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 6,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 2.5,\r\n \"MeleeResistance\": 0.38,\r\n \"ProjectileResistance\": 0.38\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 6,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 3.0,\r\n \"MeleeResistance\": 0.34,\r\n \"ProjectileResistance\": 0.34\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 6,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"RegenerationSpeed\": 3.5,\r\n \"MeleeResistance\": 0.3,\r\n \"ProjectileResistance\": 0.3\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 7,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.04,\r\n \"ProjectileResistance\": 1.04\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 7,\r\n \"ActiveLevels\": [\r\n 2\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.08,\r\n \"ProjectileResistance\": 1.08\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 7,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.12,\r\n \"ProjectileResistance\": 1.12\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 7,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.16,\r\n \"ProjectileResistance\": 1.16\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 7,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.2,\r\n \"ProjectileResistance\": 1.2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 8,\r\n \"ActiveLevels\": [\r\n 0\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.55,\r\n \"ProjectileResistance\": 0.55\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 8,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.6,\r\n \"ProjectileResistance\": 0.6\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 8,\r\n \"ActiveLevels\": [\r\n 2\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.65,\r\n \"ProjectileResistance\": 0.65\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 8,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.7,\r\n \"ProjectileResistance\": 0.7\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 8,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.75,\r\n \"ProjectileResistance\": 0.75\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 8,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.8,\r\n \"ProjectileResistance\": 0.8\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 9,\r\n \"ActiveLevels\": [\r\n 0\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.6,\r\n \"ProjectileResistance\": 0.6\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 9,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.63,\r\n \"ProjectileResistance\": 0.63\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 9,\r\n \"ActiveLevels\": [\r\n 2\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.66,\r\n \"ProjectileResistance\": 0.66\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 9,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.69,\r\n \"ProjectileResistance\": 0.69\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 9,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.72,\r\n \"ProjectileResistance\": 0.72\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 9,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.75,\r\n \"ProjectileResistance\": 0.75\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 9,\r\n \"ActiveLevels\": [\r\n 6\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.78,\r\n \"ProjectileResistance\": 0.78\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 9,\r\n \"ActiveLevels\": [\r\n 7\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.81,\r\n \"ProjectileResistance\": 0.81\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 9,\r\n \"ActiveLevels\": [\r\n 8\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.84,\r\n \"ProjectileResistance\": 0.84\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 9,\r\n \"ActiveLevels\": [\r\n 9\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.87,\r\n \"ProjectileResistance\": 0.87\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 9,\r\n \"ActiveLevels\": [\r\n 10\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 0.9,\r\n \"ProjectileResistance\": 0.9\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.02,\r\n \"ProjectileResistance\": 1.05,\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 2\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.04,\r\n \"ProjectileResistance\": 1.1,\r\n \"InfectionResistance\": 2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.06,\r\n \"ProjectileResistance\": 1.15,\r\n \"InfectionResistance\": 2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.08,\r\n \"ProjectileResistance\": 1.2,\r\n \"InfectionResistance\": 2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.10,\r\n \"ProjectileResistance\": 1.25,\r\n \"InfectionResistance\": 2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 6\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.12,\r\n \"ProjectileResistance\": 1.3,\r\n \"InfectionResistance\": 2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 7\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.14,\r\n \"ProjectileResistance\": 1.35,\r\n \"InfectionResistance\": 2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 8\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.16,\r\n \"ProjectileResistance\": 1.4,\r\n \"InfectionResistance\": 2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 9\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.18,\r\n \"ProjectileResistance\": 1.45,\r\n \"InfectionResistance\": 2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 10\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.2,\r\n \"ProjectileResistance\": 1.5,\r\n \"InfectionResistance\": 2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 11\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.22,\r\n \"ProjectileResistance\": 1.55,\r\n \"InfectionResistance\": 2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 12\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.24,\r\n \"ProjectileResistance\": 1.6,\r\n \"InfectionResistance\": 2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 13\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.26,\r\n \"ProjectileResistance\": 1.65,\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 14\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.28,\r\n \"ProjectileResistance\": 1.7,\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 11,\r\n \"ActiveLevels\": [\r\n 15\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.3,\r\n \"ProjectileResistance\": 1.75,\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 16,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"ScannerRechargeSpeed\": 1.15,\r\n \"SentryGunDamage\": 1.15,\r\n \"GlueEfficiency\": 1.15,\r\n \"TripMineDamage\": 1.15,\r\n \"FogRepellerEffect\": 1.6,\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 16,\r\n \"ActiveLevels\": [\r\n 2\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"ScannerRechargeSpeed\": 1.3,\r\n \"SentryGunDamage\": 1.3,\r\n \"GlueEfficiency\": 1.3,\r\n \"TripMineDamage\": 1.3,\r\n \"FogRepellerEffect\": 2.2,\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 16,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"ScannerRechargeSpeed\": 1.45,\r\n \"SentryGunDamage\": 1.45,\r\n \"GlueEfficiency\": 1.45,\r\n \"TripMineDamage\": 1.45,\r\n \"FogRepellerEffect\": 2.8,\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 16,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"ScannerRechargeSpeed\": 1.6,\r\n \"SentryGunDamage\": 1.6,\r\n \"GlueEfficiency\": 1.6,\r\n \"TripMineDamage\": 1.6,\r\n \"FogRepellerEffect\": 3.4,\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 16,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"ScannerRechargeSpeed\": 1.15,\r\n \"SentryGunDamage\": 1.75,\r\n \"GlueEfficiency\": 1.75,\r\n \"TripMineDamage\": 1.75,\r\n \"FogRepellerEffect\": 15.0,\r\n \"InfectionResistance\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 13,\r\n \"ActiveLevels\": [\r\n 1\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 1.1,\r\n \"ScannerRechargeSpeed\": 1.05,\r\n \"SentryGunDamage\": 1.05,\r\n \"GlueEfficiency\": 1.05,\r\n \"TripMineDamage\": 1.05,\r\n \"ComputerProcessingSpeed\": 1.05,\r\n \"FogRepellerEffect\": 1.05,\r\n \"MeleeResistance\": 1.02,\r\n \"ProjectileResistance\": 1.02,\r\n \"InfectionResistance\": 1.02\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 13,\r\n \"ActiveLevels\": [\r\n 2\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 1.2,\r\n \"ScannerRechargeSpeed\": 1.1,\r\n \"SentryGunDamage\": 1.1,\r\n \"GlueEfficiency\": 1.1,\r\n \"TripMineDamage\": 1.1,\r\n \"ComputerProcessingSpeed\": 1.1,\r\n \"FogRepellerEffect\": 1.1,\r\n \"MeleeResistance\": 1.04,\r\n \"ProjectileResistance\": 1.04,\r\n \"InfectionResistance\": 1.04\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 13,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 1.3,\r\n \"ScannerRechargeSpeed\": 1.15,\r\n \"SentryGunDamage\": 1.15,\r\n \"GlueEfficiency\": 1.15,\r\n \"TripMineDamage\": 1.15,\r\n \"ComputerProcessingSpeed\": 1.15,\r\n \"FogRepellerEffect\": 1.15,\r\n \"MeleeResistance\": 1.06,\r\n \"ProjectileResistance\": 1.06,\r\n \"InfectionResistance\": 1.06\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 13,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 1.4,\r\n \"ScannerRechargeSpeed\": 1.2,\r\n \"SentryGunDamage\": 1.2,\r\n \"GlueEfficiency\": 1.2,\r\n \"TripMineDamage\": 1.2,\r\n \"ComputerProcessingSpeed\": 1.2,\r\n \"FogRepellerEffect\": 1.2,\r\n \"MeleeResistance\": 1.08,\r\n \"ProjectileResistance\": 1.08,\r\n \"InfectionResistance\": 1.08\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 13,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 1.5,\r\n \"ScannerRechargeSpeed\": 1.25,\r\n \"SentryGunDamage\": 1.25,\r\n \"GlueEfficiency\": 1.25,\r\n \"TripMineDamage\": 1.25,\r\n \"ComputerProcessingSpeed\": 1.25,\r\n \"FogRepellerEffect\": 1.25,\r\n \"MeleeResistance\": 1.1,\r\n \"ProjectileResistance\": 1.1,\r\n \"InfectionResistance\": 1.1\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 13,\r\n \"ActiveLevels\": [\r\n 6\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 1.6,\r\n \"ScannerRechargeSpeed\": 1.3,\r\n \"SentryGunDamage\": 1.3,\r\n \"GlueEfficiency\": 1.3,\r\n \"TripMineDamage\": 1.3,\r\n \"ComputerProcessingSpeed\": 1.3,\r\n \"FogRepellerEffect\": 1.3,\r\n \"MeleeResistance\": 1.12,\r\n \"ProjectileResistance\": 1.12,\r\n \"InfectionResistance\": 1.12\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 13,\r\n \"ActiveLevels\": [\r\n 7\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 1.7,\r\n \"ScannerRechargeSpeed\": 1.35,\r\n \"SentryGunDamage\": 1.35,\r\n \"GlueEfficiency\": 1.35,\r\n \"TripMineDamage\": 1.35,\r\n \"ComputerProcessingSpeed\": 1.35,\r\n \"FogRepellerEffect\": 1.35,\r\n \"MeleeResistance\": 1.14,\r\n \"ProjectileResistance\": 1.14,\r\n \"InfectionResistance\": 1.14\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 13,\r\n \"ActiveLevels\": [\r\n 8\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 1.8,\r\n \"ScannerRechargeSpeed\": 1.4,\r\n \"SentryGunDamage\": 1.4,\r\n \"GlueEfficiency\": 1.4,\r\n \"TripMineDamage\": 1.4,\r\n \"ComputerProcessingSpeed\": 1.4,\r\n \"FogRepellerEffect\": 1.4,\r\n \"MeleeResistance\": 1.16,\r\n \"ProjectileResistance\": 1.16,\r\n \"InfectionResistance\": 1.16\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 13,\r\n \"ActiveLevels\": [\r\n 9\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 1.9,\r\n \"ScannerRechargeSpeed\": 1.45,\r\n \"SentryGunDamage\": 1.45,\r\n \"GlueEfficiency\": 1.45,\r\n \"TripMineDamage\": 1.45,\r\n \"ComputerProcessingSpeed\": 1.45,\r\n \"FogRepellerEffect\": 1.45,\r\n \"MeleeResistance\": 1.18,\r\n \"ProjectileResistance\": 1.18,\r\n \"InfectionResistance\": 1.18\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 13,\r\n \"ActiveLevels\": [\r\n 10\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"HackingProficiency\": 2.0,\r\n \"ScannerRechargeSpeed\": 1.5,\r\n \"SentryGunDamage\": 1.5,\r\n \"GlueEfficiency\": 1.5,\r\n \"TripMineDamage\": 1.5,\r\n \"ComputerProcessingSpeed\": 1.5,\r\n \"FogRepellerEffect\": 1.5,\r\n \"MeleeResistance\": 1.2,\r\n \"ProjectileResistance\": 1.2,\r\n \"InfectionResistance\": 1.2\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 14,\r\n \"ActiveLevels\": [\r\n 3\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.5,\r\n \"ProjectileResistance\": 1.5,\r\n \"InfectionResistance\": 1.5\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 14,\r\n \"ActiveLevels\": [\r\n 4\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.5,\r\n \"ProjectileResistance\": 1.5,\r\n \"InfectionResistance\": 1.5,\r\n \"RegenerationSpeed\": 2.0,\r\n \"RegenerationCap\": 2.0\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 14,\r\n \"ActiveLevels\": [\r\n 5\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.5,\r\n \"ProjectileResistance\": 1.5,\r\n \"InfectionResistance\": 1.5,\r\n \"RegenerationSpeed\": 2.0,\r\n \"RegenerationCap\": 2.0,\r\n \"ComputerProcessingSpeed\": 999,\r\n \"HackingProficiency\": 999.0,\r\n \"ScannerRechargeSpeed\": 1.5,\r\n \"SentryGunDamage\": 1.5,\r\n \"GlueEfficiency\": 1.5,\r\n \"TripMineDamage\": 1.5\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 14,\r\n \"ActiveLevels\": [\r\n 6\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.5,\r\n \"ProjectileResistance\": 1.5,\r\n \"InfectionResistance\": 1.5,\r\n \"RegenerationSpeed\": 2.0,\r\n \"RegenerationCap\": 2.0,\r\n \"ComputerProcessingSpeed\": 999,\r\n \"HackingProficiency\": 999.0,\r\n \"ScannerRechargeSpeed\": 1.5,\r\n \"SentryGunDamage\": 1.5,\r\n \"GlueEfficiency\": 1.5,\r\n \"TripMineDamage\": 1.5\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 14,\r\n \"ActiveLevels\": [\r\n 7\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.5,\r\n \"ProjectileResistance\": 1.5,\r\n \"InfectionResistance\": 1.5,\r\n \"RegenerationSpeed\": 2.0,\r\n \"RegenerationCap\": 2.0,\r\n \"ComputerProcessingSpeed\": 999,\r\n \"HackingProficiency\": 999.0,\r\n \"ScannerRechargeSpeed\": 1.5,\r\n \"SentryGunDamage\": 1.5,\r\n \"GlueEfficiency\": 1.5,\r\n \"TripMineDamage\": 1.5,\r\n \"BioscanSpeed\": 1.5\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 14,\r\n \"ActiveLevels\": [\r\n 8\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.5,\r\n \"ProjectileResistance\": 1.5,\r\n \"InfectionResistance\": 1.5,\r\n \"RegenerationSpeed\": 2.0,\r\n \"RegenerationCap\": 2.0,\r\n \"ComputerProcessingSpeed\": 999,\r\n \"HackingProficiency\": 999.0,\r\n \"ScannerRechargeSpeed\": 1.5,\r\n \"SentryGunDamage\": 1.5,\r\n \"GlueEfficiency\": 1.5,\r\n \"TripMineDamage\": 1.5,\r\n \"BioscanSpeed\": 1.5\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 14,\r\n \"ActiveLevels\": [\r\n 9\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.5,\r\n \"ProjectileResistance\": 1.5,\r\n \"InfectionResistance\": 2.0,\r\n \"RegenerationSpeed\": 2.0,\r\n \"RegenerationCap\": 2.0,\r\n \"ComputerProcessingSpeed\": 999,\r\n \"HackingProficiency\": 999.0,\r\n \"ScannerRechargeSpeed\": 1.5,\r\n \"SentryGunDamage\": 1.5,\r\n \"GlueEfficiency\": 1.5,\r\n \"TripMineDamage\": 1.5,\r\n \"BioscanSpeed\": 1.5\r\n }\r\n },\r\n {\r\n \"ClassLayoutPersistentId\": 14,\r\n \"ActiveLevels\": [\r\n 10\r\n ],\r\n \"ValueToBoosterEffects\": {\r\n \"MeleeResistance\": 1.5,\r\n \"ProjectileResistance\": 1.5,\r\n \"InfectionResistance\": 2.0,\r\n \"RegenerationSpeed\": 3.5,\r\n \"RegenerationCap\": 2.0,\r\n \"ComputerProcessingSpeed\": 999,\r\n \"HackingProficiency\": 999.0,\r\n \"ScannerRechargeSpeed\": 1.5,\r\n \"SentryGunDamage\": 1.5,\r\n \"GlueEfficiency\": 1.5,\r\n \"TripMineDamage\": 1.5,\r\n \"BioscanSpeed\": 1.5\r\n }\r\n }\r\n]\r\n"; public const string Groups = "\r\n[\r\n {\r\n \"PersistentId\": 0,\r\n \"VisibleForPlayerCount\": [\r\n 1,\r\n 2,\r\n 3,\r\n 4\r\n ],\r\n \"Name\": \"Combat\"\r\n },\r\n {\r\n \"PersistentId\": 1,\r\n \"VisibleForPlayerCount\": [\r\n 1,\r\n 2,\r\n 3,\r\n 4\r\n ],\r\n \"Name\": \"Survival\"\r\n },\r\n {\r\n \"PersistentId\": 2,\r\n \"VisibleForPlayerCount\": [\r\n 1,\r\n 2,\r\n 3,\r\n 4\r\n ],\r\n \"Name\": \"Utility\"\r\n },\r\n {\r\n \"PersistentId\": 3,\r\n \"VisibleForPlayerCount\": [\r\n 1\r\n ],\r\n \"Name\": \"Solo\"\r\n }\r\n]\r\n"; public const string ClassLayouts = "\r\n[\r\n {\r\n \"Header\": \"Generalist\",\r\n \"GroupPersistentId\": 2,\r\n \"PersistentId\": 13,\r\n \"InfoText\": \"Good at everything, best at nothing.\\nMax LV 10 Bonus: Double Jump\\n\\nBonuses Each LV up:\\n+2% Damage\\n+2% Armor\\n+2% Infection Resist\\n+10% Hack Skill\\n-5% Terminal Delay\\n+5% Tool Power\\n+5% Repeller Power\\n2% Ammo Refill\\n2% Tool Refill\\n4% Heal\\n1% Speed\",\r\n \"StartingBuffs\": [],\r\n \"Levels\": [\r\n {\r\n \"LevelNumber\": 0,\r\n \"TotalXpRequired\": 0,\r\n \"CustomLevelUpPopupText\": null,\r\n \"CustomLevelStatsText\": \"Class: Generalist\\n{0}\\nDamage 100% -> 102%\\nArmor 100% -> 102%\\nInfect Res. 0% -> 2%\\nHacking 100% - > 110%\\nTerminal Delay 100% -> 95%\\nTool Power 100% -> 105%\\nRepeller 100% -> 105%\\nSpeed 100% -> 101%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.0,\r\n \"WeaponDamageMultiplier\": 1.0,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 1,\r\n \"TotalXpRequired\": 300,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\n2% Ammo Refill\\n2% Tool Refill\\n4% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Generalist\\n{0}\\nDamage 102% -> 104%\\nArmor 102% -> 104%\\nInfect Res. 2% -> 4%\\nHacking 110% - > 120%\\nTerminal Delay 95% -> 90%\\nTool Power 105% -> 110%\\nRepeller 105% -> 110%\\nSpeed 101% -> 102%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.02,\r\n \"WeaponDamageMultiplier\": 1.02,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.01\r\n }\r\n ],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionTool\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.02\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 2,\r\n \"TotalXpRequired\": 800,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\n2% Ammo Refill\\n2% Tool Refill\\n4% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Generalist\\n{0}\\nDamage 104% -> 106%\\nArmor 104% -> 106%\\nInfect Res. 4% -> 6%\\nHacking 120% - > 130%\\nTerminal Delay 90% -> 85%\\nTool Power 110% -> 115%\\nRepeller 110% -> 115%\\nSpeed 102% -> 103%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.04,\r\n \"WeaponDamageMultiplier\": 1.04,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.02\r\n }\r\n ],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionTool\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.02\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 3,\r\n \"TotalXpRequired\": 1450,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\n2% Ammo Refill\\n2% Tool Refill\\n4% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Generalist\\n{0}\\nDamage 106% -> 108%\\nArmor 106% -> 108%\\nInfect Res. 6% -> 8%\\nHacking 130% - > 140%\\nTerminal Delay 85% -> 80%\\nTool Power 115% -> 120%\\nRepeller 115% -> 120%\\nSpeed 103% -> 104%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.06,\r\n \"WeaponDamageMultiplier\": 1.06,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.03\r\n }\r\n ],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionTool\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.02\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 4,\r\n \"TotalXpRequired\": 2200,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\n2% Ammo Refill\\n2% Tool Refill\\n4% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Generalist\\n{0}\\nDamage 108% -> 110%\\nArmor 108% -> 110%\\nInfect Res. 8% -> 10%\\nHacking 140% - > 150%\\nTerminal Delay 80% -> 75%\\nTool Power 120% > 125%\\nRepeller 120% -> 125%\\nSpeed 104% -> 105%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.08,\r\n \"WeaponDamageMultiplier\": 1.08,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.04\r\n }\r\n ],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionTool\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.02\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 5,\r\n \"TotalXpRequired\": 3025,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\n2% Ammo Refill\\n2% Tool Refill\\n4% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Generalist\\n{0}\\nDamage 110% -> 112%\\nArmor 110% -> 112%\\nInfect Res. 10% -> 12%\\nHacking 150% -> 160%\\nTerminal Delay 75% -> 70%\\nTool Power 125% -> 130%\\nRepeller 125% -> 130%\\nSpeed 105% -> 106%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.1,\r\n \"WeaponDamageMultiplier\": 1.1,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.05\r\n }\r\n ],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionTool\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.02\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 6,\r\n \"TotalXpRequired\": 3975,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\n2% Ammo Refill\\n2% Tool Refill\\n4% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Generalist\\n{0}\\nDamage 112% -> 114%\\nArmor 112% -> 114%\\nInfect Res. 12% -> 14%\\nHacking 160% -> 170%\\nTerminal Delay 70% -> 65%\\nTool Power 130% -> 135%\\nRepeller 130% -> 135%\\nSpeed 106% -> 107%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.12,\r\n \"WeaponDamageMultiplier\": 1.12,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.06\r\n }\r\n ],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionTool\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.02\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 7,\r\n \"TotalXpRequired\": 5100,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\n2% Ammo Refill\\n2% Tool Refill\\n4% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Generalist\\n{0}\\nDamage 114% -> 116%\\nArmor 114% -> 116%\\nInfect Res. 14% -> 16%\\nHacking 170% -> 180%\\nTerminal Delay 65% -> 60%\\nTool Power 135% -> 140%\\nRepeller 135% -> 140%\\nSpeed 107% -> 108%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.14,\r\n \"WeaponDamageMultiplier\": 1.14,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.07\r\n }\r\n ],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionTool\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.02\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 8,\r\n \"TotalXpRequired\": 6300,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\n2% Ammo Refill\\n2% Tool Refill\\n4% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Generalist\\n{0}\\nDamage 116% -> 118%\\nArmor 116% -> 118%\\nInfect Res. 16% -> 18%\\nHacking 180% -> 190%\\nTerminal Delay 60% -> 55%\\nTool Power 140% -> 145%\\nRepeller 140% -> 145%\\nSpeed 108% -> 109%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.16,\r\n \"WeaponDamageMultiplier\": 1.16,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.08\r\n }\r\n ],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionTool\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.02\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 9,\r\n \"TotalXpRequired\": 7575,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\n2% Ammo Refill\\n2% Tool Refill\\n4% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Generalist\\n{0}\\nDamage 118% -> 120%\\nArmor 118% -> 120%\\nInfect Res. 18% -> 20%\\nHacking 190% -> 200%\\nTerminal Delay 55% -> 50%\\nTool Power 145% -> 150%\\nRepeller 145% -> 150%\\nSpeed 109% -> 110%\\nJump -> Double Jump\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.18,\r\n \"WeaponDamageMultiplier\": 1.18,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.09\r\n }\r\n ],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionTool\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.02\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 10,\r\n \"TotalXpRequired\": 8925,\r\n \"CustomLevelUpPopupText\": \"MAX LV!\\n+Double Jump\\n2% Ammo Refill\\n2% Tool Refill\\n4% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Generalist\\n{0}\\nDamage 120%\\nArmor 120%\\nInfect Res. 20%\\nHacking 200%\\nTerminal Delay 50%\\nTool Power 150%\\nRepeller 150%\\nSpeed 110%\\nDouble Jump\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.2,\r\n \"WeaponDamageMultiplier\": 1.2,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.1\r\n }\r\n ],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionTool\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.02\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.02\r\n }\r\n ]\r\n }\r\n ]\r\n },\r\n {\r\n \"Header\": \"Soldier\",\r\n \"GroupPersistentId\": 0,\r\n \"PersistentId\": 2,\r\n \"InfoText\": \"Specialized in ranged combat. Stronger guns with ammo refunds on each level up.\\nMax LV 10 bonus: +35% HP Regen Speed\\n\\nBonuses Each LV up:\\n+4% Gun Damage\\n+4% Ammo Refill\\n\\nBonus Each 5 LVs:\\n+6% Ammo Refill\",\r\n \"StartingBuffs\": [],\r\n \"Levels\": [\r\n {\r\n \"LevelNumber\": 0,\r\n \"TotalXpRequired\": 0,\r\n \"CustomLevelUpPopupText\": null,\r\n \"CustomLevelStatsText\": \"Class: Soldier\\n{0}\\nGun Damage 100% -> 104%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 1,\r\n \"TotalXpRequired\": 100,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +4%\\n4% Ammo Refill\",\r\n \"CustomLevelStatsText\": \"Class: Soldier\\n{0}\\nGun Damage 104% -> 108%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.04,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.04\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 2,\r\n \"TotalXpRequired\": 600,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +4%\\n4% Ammo Refill\",\r\n \"CustomLevelStatsText\": \"Class: Soldier\\n{0}\\nGun Damage 108% -> 112%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.08,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.04\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 3,\r\n \"TotalXpRequired\": 1200,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +4%\\n4% Ammo Refill\",\r\n \"CustomLevelStatsText\": \"Class: Soldier\\n{0}\\nGun Damage 112% -> 116%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.12,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.04\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 4,\r\n \"TotalXpRequired\": 1900,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +4%\\n4% Ammo Refill\",\r\n \"CustomLevelStatsText\": \"Class: Soldier\\n{0}\\nGun Damage 116% -> 120%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.16,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.04\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 5,\r\n \"TotalXpRequired\": 2700,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +4%\\n10% Ammo Refill\",\r\n \"CustomLevelStatsText\": \"Class: Soldier\\n{0}\\nGun Damage 120% -> 124%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.2,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.1\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 6,\r\n \"TotalXpRequired\": 3600,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +4%\\n4% Ammo Refill\",\r\n \"CustomLevelStatsText\": \"Class: Soldier\\n{0}\\nGun Damage 124% -> 128%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.24,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.04\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 7,\r\n \"TotalXpRequired\": 4600,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +4%\\n4% Ammo Refill\",\r\n \"CustomLevelStatsText\": \"Class: Soldier\\n{0}\\nGun Damage 128% -> 132%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.28,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.04\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 8,\r\n \"TotalXpRequired\": 5700,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +4%\\n4% Ammo Refill\",\r\n \"CustomLevelStatsText\": \"Class: Soldier\\n{0}\\nGun Damage 132% -> 136%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.32,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.04\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 9,\r\n \"TotalXpRequired\": 6900,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +4%\\n4% Ammo Refill\",\r\n \"CustomLevelStatsText\": \"Class: Soldier\\n{0}\\nGun Damage 136% -> 140%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.36,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.04\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.04\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 10,\r\n \"TotalXpRequired\": 8200,\r\n \"CustomLevelUpPopupText\": \"MAX LV!\\nGun Damage +4%\\n10% Ammo Refill\\nRegen Speed +35%\",\r\n \"CustomLevelStatsText\": \"Class: Soldier\\n{0}\\nGun Damage 140%\\nRegen Speed 135%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.4,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"AmmunitionMain\",\r\n \"Value\": 0.1\r\n },\r\n {\r\n \"SingleBuff\": \"AmmunitionSpecial\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n }\r\n ]\r\n },\r\n {\r\n \"Header\": \"Marine\",\r\n \"GroupPersistentId\": 0,\r\n \"PersistentId\": 9,\r\n \"InfoText\": \"Specialized in ranged combat. Powerful weapons but with poor Armor.\\n<color=red>Starting Debuff: -40% Armor</color>\\nMax LV 10\\n\\nBonuses Each LV up:\\n+8% Gun Damage\\n+3% Armor\",\r\n \"StartingBuffs\": [],\r\n \"Levels\": [\r\n {\r\n \"LevelNumber\": 0,\r\n \"TotalXpRequired\": 0,\r\n \"CustomLevelUpPopupText\": null,\r\n \"CustomLevelStatsText\": \"Class: Marine\\n{0}\\nGun Damage 100% -> 108%\\nArmor 60% -> 63%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 1,\r\n \"TotalXpRequired\": 85,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +8%\\nArmor +3%\",\r\n \"CustomLevelStatsText\": \"Class: Marine\\n{0}\\nGun Damage 108% -> 116%\\nArmor 63% -> 66%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.08,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 2,\r\n \"TotalXpRequired\": 510,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +8%\\nArmor +3%\",\r\n \"CustomLevelStatsText\": \"Class: Marine\\n{0}\\nGun Damage 116% -> 124%\\nArmor 66% -> 69%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.16,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 3,\r\n \"TotalXpRequired\": 1020,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +8%\\nArmor +3%\",\r\n \"CustomLevelStatsText\": \"Class: Marine\\n{0}\\nGun Damage 124% -> 132%\\nArmor 69% -> 72%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.24,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 4,\r\n \"TotalXpRequired\": 1615,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +8%\\nArmor +3%\",\r\n \"CustomLevelStatsText\": \"Class: Marine\\n{0}\\nGun Damage 132% -> 140%\\nArmor 72% -> 75%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.32,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 5,\r\n \"TotalXpRequired\": 2295,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +8%\\nArmor +3%\",\r\n \"CustomLevelStatsText\": \"Class: Marine\\n{0}\\nGun Damage 140% -> 148%\\nArmor 75% -> 78%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.4,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 6,\r\n \"TotalXpRequired\": 3060,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +8%\\nArmor +3%\",\r\n \"CustomLevelStatsText\": \"Class: Marine\\n{0}\\nGun Damage 148% -> 156%\\nArmor 78% -> 81%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.48,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 7,\r\n \"TotalXpRequired\": 3910,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +8%\\nArmor +3%\",\r\n \"CustomLevelStatsText\": \"Class: Marine\\n{0}\\nGun Damage 156% -> 164%\\nArmor 81% -> 84%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.56,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 8,\r\n \"TotalXpRequired\": 4845,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +8%\\nArmor +3%\",\r\n \"CustomLevelStatsText\": \"Class: Marine\\n{0}\\nGun Damage 164% -> 172%\\nArmor 84% -> 87%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.64,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 9,\r\n \"TotalXpRequired\": 5865,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +8%\\nArmor +3%\",\r\n \"CustomLevelStatsText\": \"Class: Marine\\n{0}\\nGun Damage 172% -> 180%\\nArmor 87% -> 90%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.72,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 10,\r\n \"TotalXpRequired\": 6970,\r\n \"CustomLevelUpPopupText\": \"MAX LV!\\nGun Damage +8%\\nArmor +3%\",\r\n \"CustomLevelStatsText\": \"Class: Marine\\n{0}\\nGun Damage 180%\\nArmor 90%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1.8,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n }\r\n ]\r\n },\r\n {\r\n \"Header\": \"Hunter\",\r\n \"GroupPersistentId\": 0,\r\n \"PersistentId\": 7,\r\n \"InfoText\": \"Specialized in melee combat. Deal high melee damage while gaining light Armor for survivability.\\nMax LV 5 bonus: Double Jump\\nLV 1 bonus: 125% Melee Damage\\n\\nBonuses Each LV up:\\n+4% Armor\\n+4% Speed\",\r\n \"StartingBuffs\": [],\r\n \"Levels\": [\r\n {\r\n \"LevelNumber\": 0,\r\n \"TotalXpRequired\": 0,\r\n \"CustomLevelUpPopupText\": null,\r\n \"CustomLevelStatsText\": \"Class: Hunter\\n{0}\\nMelee 100% -> 225%\\nArmor 100% -> 104%\\nSpeed 100% -> 104%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.0,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 1,\r\n \"TotalXpRequired\": 450,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee +125%\\nArmor +4%\\nSpeed +4%\",\r\n \"CustomLevelStatsText\": \"Class: Hunter\\n{0}\\nMelee 225%\\nArmor 104% -> 108%\\nSpeed 104% -> 108%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 2.25,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.04\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 2,\r\n \"TotalXpRequired\": 1150,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +4%\\nSpeed +4%\",\r\n \"CustomLevelStatsText\": \"Class: Hunter\\n{0}\\nMelee 225%\\nArmor 108% -> 112%\\nSpeed 108% -> 112%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 2.25,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.08\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 3,\r\n \"TotalXpRequired\": 2200,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +4%\\nSpeed +4%\",\r\n \"CustomLevelStatsText\": \"Class: Hunter\\n{0}\\nMelee 225%\\nArmor 112% -> 116%\\nSpeed 112% -> 116%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 2.25,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.2\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 4,\r\n \"TotalXpRequired\": 3700,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +4%\\nSpeed +4%\",\r\n \"CustomLevelStatsText\": \"Class: Hunter\\n{0}\\nMelee 225%\\nArmor 116% -> 120%\\nSpeed 116% -> 120%\\nJump -> Double Jump\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 2.25,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.16\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 5,\r\n \"TotalXpRequired\": 5650,\r\n \"CustomLevelUpPopupText\": \"MAX LV!\\nArmor +4%\\nSpeed +4%\\n+Double Jump\",\r\n \"CustomLevelStatsText\": \"Class: Hunter\\n{0}\\nMelee 225%\\nArmor 120%\\nSpeed 120%\\nDouble Jump\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 2.25,\r\n \"WeaponDamageMultiplier\": 1.0,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.2\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n }\r\n ]\r\n },\r\n {\r\n \"Header\": \"Assassin\",\r\n \"GroupPersistentId\": 0,\r\n \"PersistentId\": 8,\r\n \"InfoText\": \"Specialized in melee combat. Devastating in stealth and combat but with very poor survivability.\\n<color=red>Starting Debuff: -45% Armor</color>\\nMax LV 5 bonus: Double Jump\\nLV 1 bonus: 215% Melee Damage\\n\\nBonuses Each LV up:\\n+5% Armor\\n+4% Speed\",\r\n \"StartingBuffs\": [],\r\n \"Levels\": [\r\n {\r\n \"LevelNumber\": 0,\r\n \"TotalXpRequired\": 0,\r\n \"CustomLevelUpPopupText\": null,\r\n \"CustomLevelStatsText\": \"Class: Assassin\\n{0}\\nMelee 100% -> 315%\\nArmor 55% -> 60%\\nSpeed 100% -> 104%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.0,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.0\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 1,\r\n \"TotalXpRequired\": 450,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee +215%\\nArmor +5%\\nSpeed +4%\",\r\n \"CustomLevelStatsText\": \"Class: Assassin\\n{0}\\nMelee 315%\\nArmor 60% -> 65%\\nSpeed 104% -> 108%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 3.15,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.04\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 2,\r\n \"TotalXpRequired\": 1150,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +5%\\nSpeed +4%\",\r\n \"CustomLevelStatsText\": \"Class: Assassin\\n{0}\\nMelee 315%\\nArmor 65% -> 70%\\nSpeed 108% -> 112%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 3.15,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.08\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 3,\r\n \"TotalXpRequired\": 2200,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +5%\\nSpeed +4%\",\r\n \"CustomLevelStatsText\": \"Class: Assassin\\n{0}\\nMelee 315%\\nArmor 70% -> 75%\\nSpeed 112% -> 116%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 3.15,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.12\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 4,\r\n \"TotalXpRequired\": 3700,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +5%\\nSpeed +4%\",\r\n \"CustomLevelStatsText\": \"Class: Assassin\\n{0}\\nMelee 315%\\nArmor 75% -> 80%\\nSpeed 116% -> 120%\\nJump -> Double Jump\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 3.15,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.16\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 5,\r\n \"TotalXpRequired\": 5650,\r\n \"CustomLevelUpPopupText\": \"MAX LV!\\nArmor +5%\\nSpeed +4%\\n+Double Jump\",\r\n \"CustomLevelStatsText\": \"Class: Assassin\\n{0}\\nMelee 315%\\nArmor 80%\\nSpeed 120%\\nDouble Jump\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 3.15,\r\n \"WeaponDamageMultiplier\": 1.0,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.2\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n }\r\n ]\r\n },\r\n {\r\n \"Header\": \"Fighter\",\r\n \"GroupPersistentId\": 0,\r\n \"PersistentId\": 10,\r\n \"InfoText\": \"Specialized in general combat. Improved damage with all guns and melee.\\nMax LV 10\\n\\nBonuses Each LV up:\\n+3% Gun Damage\\n+6% Melee Damage\\n+1% Speed\",\r\n \"StartingBuffs\": [],\r\n \"Levels\": [\r\n {\r\n \"LevelNumber\": 0,\r\n \"TotalXpRequired\": 0,\r\n \"CustomLevelUpPopupText\": null,\r\n \"CustomLevelStatsText\": \"Class: Fighter\\n{0}\\nGun Damage 100% -> 103%\\nMelee Damage 100% -> 106%\\nSpeed 100% -> 101%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 1,\r\n \"TotalXpRequired\": 100,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +3%\\nMelee Damage +6%\\nSpeed +1%\",\r\n \"CustomLevelStatsText\": \"Class: Fighter\\n{0}\\nGun Damage 103% -> 106%\\nMelee Damage 106% -> 112%\\nSpeed 101% -> 102%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.06,\r\n \"WeaponDamageMultiplier\": 1.03,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.01\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 2,\r\n \"TotalXpRequired\": 600,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +3%\\nMelee Damage +7%\\nSpeed +1%\",\r\n \"CustomLevelStatsText\": \"Class: Fighter\\n{0}\\nGun Damage 106% -> 109%\\nMelee Damage 112% -> 118%\\nSpeed 102% -> 103%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.12,\r\n \"WeaponDamageMultiplier\": 1.06,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.02\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 3,\r\n \"TotalXpRequired\": 1200,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +3%\\nMelee Damage +7%\\nSpeed +1%\",\r\n \"CustomLevelStatsText\": \"Class: Fighter\\n{0}\\nGun Damage 109% -> 112%\\nMelee Damage 118% -> 124%\\nSpeed 103% -> 104%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.18,\r\n \"WeaponDamageMultiplier\": 1.09,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.03\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 4,\r\n \"TotalXpRequired\": 1900,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +3%\\nMelee Damage +7%\\nSpeed +1%\",\r\n \"CustomLevelStatsText\": \"Class: Fighter\\n{0}\\nGun Damage 112% -> 115%\\nMelee Damage 124% -> 130%\\nSpeed 104% -> 105%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.24,\r\n \"WeaponDamageMultiplier\": 1.12,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.04\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 5,\r\n \"TotalXpRequired\": 2700,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +3%\\nMelee Damage +7%\\nSpeed +1%\",\r\n \"CustomLevelStatsText\": \"Class: Fighter\\n{0}\\nGun Damage 115% -> 118%\\nMelee Damage 130% -> 136%\\nSpeed 105% -> 106%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.3,\r\n \"WeaponDamageMultiplier\": 1.15,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.05\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 6,\r\n \"TotalXpRequired\": 3600,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +3%\\nMelee Damage +7%\\nSpeed +1%\",\r\n \"CustomLevelStatsText\": \"Class: Fighter\\n{0}\\nGun Damage 118% -> 121%\\nMelee Damage 136% -> 142%\\nSpeed 106% -> 107%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.36,\r\n \"WeaponDamageMultiplier\": 1.18,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.06\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 7,\r\n \"TotalXpRequired\": 4600,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +3%\\nMelee Damage +7%\\nSpeed +1%\",\r\n \"CustomLevelStatsText\": \"Class: Fighter\\n{0}\\nGun Damage 121% -> 124%\\nMelee Damage 142% -> 148%\\nSpeed 107% -> 108%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.42,\r\n \"WeaponDamageMultiplier\": 1.21,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.07\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 8,\r\n \"TotalXpRequired\": 5700,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +3%\\nMelee Damage +7%\\nSpeed +1%\",\r\n \"CustomLevelStatsText\": \"Class: Fighter\\n{0}\\nGun Damage 124% -> 127%\\nMelee Damage 148% -> 154%\\nSpeed 108% -> 109%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.48,\r\n \"WeaponDamageMultiplier\": 1.24,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.08\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 9,\r\n \"TotalXpRequired\": 6900,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nGun Damage +3%\\nMelee Damage +7%\\nSpeed +1%\",\r\n \"CustomLevelStatsText\": \"Class: Fighter\\n{0}\\nGun Damage 127% -> 130%\\nMelee Damage 154% -> 160%\\nSpeed 109% -> 110%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.54,\r\n \"WeaponDamageMultiplier\": 1.27,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.09\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 10,\r\n \"TotalXpRequired\": 8200,\r\n \"CustomLevelUpPopupText\": \"MAX LV!\\nGun Damage +3%\\nMelee Damage +7%\\nSpeed +1%\",\r\n \"CustomLevelStatsText\": \"Class: Fighter\\n{0}\\nGun Damage 130%\\nMelee Damage 160%\\nSpeed 110%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1.6,\r\n \"WeaponDamageMultiplier\": 1.3,\r\n \"CustomScaling\": [\r\n {\r\n \"CustomBuff\": \"MovementSpeedMultiplier\",\r\n \"Value\": 1.1\r\n }\r\n ],\r\n \"LevelUpBonus\": []\r\n }\r\n ]\r\n },\r\n {\r\n \"Header\": \"Survivalist\",\r\n \"GroupPersistentId\": 1,\r\n \"PersistentId\": 3,\r\n \"InfoText\": \"Increased odds of survival in extreme conditions. Endure any scenario.\\nMax LV 15 bonus: Infect Immune\\n\\nBonuses Each LV up:\\n+3% Armor\\n+3% Infect Resist\\n10% Heal\",\r\n \"StartingBuffs\": [],\r\n \"Levels\": [\r\n {\r\n \"LevelNumber\": 0,\r\n \"TotalXpRequired\": 0,\r\n \"CustomLevelUpPopupText\": null,\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 100% -> 103%\\nInfect Res. 0% -> 3%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 1,\r\n \"TotalXpRequired\": 100,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 103% -> 106%\\nInfect Res. 0% -> 3%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 2,\r\n \"TotalXpRequired\": 600,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 106% -> 109%\\nInfect Res. 3% -> 6%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 3,\r\n \"TotalXpRequired\": 1200,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 109% -> 112%\\nInfect Res. 6% -> 9%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 4,\r\n \"TotalXpRequired\": 1850,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 112% -> 115%\\nInfect Res. 9% -> 12%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 5,\r\n \"TotalXpRequired\": 2550,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n20% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 115% -> 118%\\nInfect Res. 12% -> 15%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.2\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 6,\r\n \"TotalXpRequired\": 3300, \r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 118% -> 121%\\nInfect Res. 15% -> 18%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 7,\r\n \"TotalXpRequired\": 4100, \r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 121% -> 124%\\nInfect Res. 18% -> 21%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 8,\r\n \"TotalXpRequired\": 4950, \r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 124% -> 127%\\nInfect Res. 21% -> 24%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 9,\r\n \"TotalXpRequired\": 5850, \r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 127% -> 130%\\nInfect Res. 24% -> 27%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 10,\r\n \"TotalXpRequired\": 6850, \r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n20% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 130% -> 133%\\nInfect Res. 27% -> 30%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.2\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 11,\r\n \"TotalXpRequired\": 7950,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 133% -> 136%\\nInfect Res. 30% -> 33%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 12,\r\n \"TotalXpRequired\": 9150,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 136% -> 139%\\nInfect Res. 33% -> 36%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 13,\r\n \"TotalXpRequired\": 10450,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 139% -> 142%\\nInfect Res. 36% -> 39%\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 14,\r\n \"TotalXpRequired\": 11850,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nArmor +3%\\nInfection Res. +3%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 142% -> 145%\\nInfect Res. 39% -> Immune\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 15,\r\n \"TotalXpRequired\": 13350,\r\n \"CustomLevelUpPopupText\": \"MAX LV!\\nArmor +3%\\nInfection Res. +61%\\n20% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Survivalist\\n{0}\\nArmor 145%\\nInfect Immune\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.2\r\n }\r\n ]\r\n }\r\n ]\r\n },\r\n {\r\n \"Header\": \"Outlander\",\r\n \"GroupPersistentId\": 1,\r\n \"PersistentId\": 11,\r\n \"InfoText\": \"Specialized for surviving on the run. Greatly improved Projectile Armor and Infect Resist.\\nMax LV 15\\nLV 1 bonus: Infect Immune\\n\\nBonuses Each LV up:\\n+2% Melee Armor\\n+5% Projectile Armor\\n10% Heal\",\r\n \"StartingBuffs\": [],\r\n \"Levels\": [\r\n {\r\n \"LevelNumber\": 0,\r\n \"TotalXpRequired\": 0,\r\n \"CustomLevelUpPopupText\": null,\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 100% -> 102%\\nProjectile Armor 100% -> 105%\\nInfect Res. 0% -> Immune\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": []\r\n },\r\n {\r\n \"LevelNumber\": 1,\r\n \"TotalXpRequired\": 100,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee Armor +2%\\nProjectile Armor +5%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 102% -> 104%\\nProjectile Armor 105% -> 110%\\nInfect Immune\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 2,\r\n \"TotalXpRequired\": 600,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee Armor +2%\\nProjectile Armor +5%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 104% -> 106%\\nProjectile Armor 110% -> 115%\\nInfect Immune\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 3,\r\n \"TotalXpRequired\": 1200,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee Armor +2%\\nProjectile Armor +5%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 106% -> 108%\\nProjectile Armor 115% -> 120%\\nInfect Immune\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 4,\r\n \"TotalXpRequired\": 1850,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee Armor +2%\\nProjectile Armor +5%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 108% -> 110%\\nProjectile Armor 120% -> 125%\\nInfect Immune\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 5,\r\n \"TotalXpRequired\": 2550,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee Armor +2%\\nProjectile Armor +5%\\n20% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 110% -> 112%\\nProjectile Armor 125% -> 130%\\nInfect Immune\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.2\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 6,\r\n \"TotalXpRequired\": 3300, \r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee Armor +2%\\nProjectile Armor +5%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 112% -> 114%\\nProjectile Armor 130% -> 135%\\nInfect Immune\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 7,\r\n \"TotalXpRequired\": 4100, \r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee Armor +2%\\nProjectile Armor +5%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 114% -> 116%\\nProjectile Armor 135% -> 140%\\nInfect Immune\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 8,\r\n \"TotalXpRequired\": 4950, \r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee Armor +2%\\nProjectile Armor +5%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 116% -> 118%\\nProjectile Armor 140% -> 145%\\nInfect Immune\",\r\n \"HealthMultiplier\": 1,\r\n \"MeleeDamageMultiplier\": 1,\r\n \"WeaponDamageMultiplier\": 1,\r\n \"CustomScaling\": [],\r\n \"LevelUpBonus\": [\r\n {\r\n \"SingleBuff\": \"Heal\",\r\n \"Value\": 0.1\r\n }\r\n ]\r\n },\r\n {\r\n \"LevelNumber\": 9,\r\n \"TotalXpRequired\": 5850, \r\n \"C