Decompiled source of GTFuckingXP v2.3.1
EndskApi.dll
Decompiled 2 weeks 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.EnemyKill; 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+git2188966-dirty-dinorush-changes.21889663f7de81bdeef03a300b44337f0893adc7")] [assembly: AssemblyProduct("EndskApi")] [assembly: AssemblyTitle("EndskApi")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace EndskApi { [BepInPlugin("Endskill.EndskApi", "EndskApi", "1.2.0")] 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.2.0"; 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 = "git2188966-dirty-dinorush-changes"; public const string SemVer = "1.0.0+git2188966-dirty-dinorush-changes"; public const string GitRevShort = "2188966-dirty"; public const string GitRevLong = "21889663f7de81bdeef03a300b44337f0893adc7-dirty"; public const string GitBranch = "dinorush-changes"; 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] = (((GameDataBlockBase<EnemyDataBlock>)(object)allBlocks[i]).persistentID, ((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); } public void RemoveMiddleMouseClickTool(IExtendedTool Tool) { _middleMouseClickTools.Remove(Tool); } public void AddTimedTool(IExtendedTool tool) { _timedTools.Add(tool); } public void RemoveTimedTool(IExtendedTool tool) { _timedTools.Remove(tool); } public void AddHiddenTool(Tool tool) { _hiddenTools.Add(tool); } 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; } private object GetInformationPackage(InformationId id) { return id switch { InformationId.None => null, InformationId.EnemyId => _enemyNamesIdMap[CurrentEnemyIndex], _ => null, }; } 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); } 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.EnemyKill { [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(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 { [HarmonyPatch("StoreCheckpoint")] [HarmonyPostfix] public static void StoreCheackpointPostfix() { CheckpointApi.InvokeCheckpointReachedCallbacks(); NetworkManager.SendCheckpointReached(); } [HarmonyPatch("OnLevelCleanup")] [HarmonyPostfix] public static void OnLevelCleanupPostfix() { 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)) { return (T)value; } 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)) { info = (T)value; return true; } 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"; public static void Setup() { NetworkAPI.RegisterEvent<DummyStruct>("CheckpointCleanup", (Action<ulong, DummyStruct>)ReceiveCheckpointCleanup); NetworkAPI.RegisterEvent<DummyStruct>("CheckpointHasBeenReached", (Action<ulong, DummyStruct>)ReceiveCheckpointReached); } private static void ReceiveCheckpointReached(ulong snetPlayer, DummyStruct _) { CheckpointApi.InvokeCheckpointReachedCallbacks(); } private static void ReceiveCheckpointCleanup(ulong snetPlayer, DummyStruct _) { CheckpointApi.InvokeCheckpointCleanupCallbacks(); } 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 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 List<DamageDistribution> DamageDistributions { get; set; } public PlayerAgent LastHitDealtBy { get; set; } public LastHitType lastHitType { get; set; } public EnemyKillDistribution(EnemyAgent agentToKill) { KilledEnemyAgent = agentToKill; DamageDistributions = new List<DamageDistribution>(); } public void AddDamageDealtByPlayerAgent(PlayerAgent agent, float damage) { DamageDistributions.Add(new DamageDistribution(agent.PlayerSlotIndex, damage)); } public float GetDamageDealtBySnet(SNet_Player player) { int slotIndex = player.PlayerSlotIndex(); return DamageDistributions.Where((DamageDistribution it) => it.PlayerSlotIndex == slotIndex).Sum((DamageDistribution it) => it.DamageDealt); } } 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 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 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 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 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)); 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>(); 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); CacheApi.SaveInstance(new Dictionary<IntPtr, EnemyKillDistribution>(), "EndskApi"); LevelApi.AddEndLevelCallback(delegate { _enemyStates = new Dictionary<IntPtr, bool>(); CacheApi.SaveInstance(new Dictionary<IntPtr, EnemyKillDistribution>(), "EndskApi"); }); _setup = true; } } private static void ReceiveMeleePrefix(Dam_EnemyDamageBase instance, ref pFullDamageData data) { LogManager.Debug($"Receive Melee EnemyKillApi Prefix State = {((Agent)instance.Owner).Alive}"); _enemyStates[((Il2CppObjectBase)instance).Pointer] = ((Agent)instance.Owner).Alive; } private static void ReceiveMeleePostfix(Dam_EnemyDamageBase instance, ref pFullDamageData data) { LogManager.Debug($"Receive Melee EnemyKillApi Postfix State = {((Agent)instance.Owner).Alive}"); 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) { _enemyStates[((Il2CppObjectBase)instance).Pointer] = ((Agent)instance.Owner).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) { _enemyStates[((Il2CppObjectBase)instance).Pointer] = ((Agent)instance.Owner).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) { Dictionary<IntPtr, EnemyKillDistribution> instance = CacheApi.GetInstance<Dictionary<IntPtr, EnemyKillDistribution>>("EndskApi"); if (!instance.ContainsKey(((Il2CppObjectBase)hitEnemy).Pointer)) { EnemyKillDistribution enemyKillDistribution = new EnemyKillDistribution(hitEnemy); enemyKillDistribution.AddDamageDealtByPlayerAgent(damageDealer, damageDealt); instance[((Il2CppObjectBase)hitEnemy).Pointer] = enemyKillDistribution; } else { instance[((Il2CppObjectBase)hitEnemy).Pointer].AddDamageDealtByPlayerAgent(damageDealer, damageDealt); } } private static void EnemyDied(EnemyAgent hitEnemy, PlayerAgent lastHit, LastHitType lastHitType) { if (CacheApi.GetInstance<Dictionary<IntPtr, EnemyKillDistribution>>("EndskApi").TryGetValue(((Il2CppObjectBase)hitEnemy).Pointer, out var value)) { value.LastHitDealtBy = lastHit; value.lastHitType = lastHitType; InvokeEnemyKilledCallbacks(value); } } } 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) GearInfo info2 = info; PlayerBackpack localBackpack = PlayerBackpackManager.LocalBackpack; localBackpack.SpawnAndEquipGearAsync(info2.Slot, info2.GearId, delBackpackItemCallback.op_Implicit((Action<BackpackItem>)BackPackItemCreatedCallBack)); SetAmmoStorage(info2, 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(info2.AmmunitionInMagazine); PlayerManager.GetLocalPlayerAgent().Sync.WantsToWieldSlot(info2.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 weeks 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.Versioning; using System.Text.Json; using AIGraph; using Agents; using BepInEx; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using DoubleJump; using EndskApi.Api; using Enemies; using GTFuckingXP.Communication; using GTFuckingXP.Extensions; using GTFuckingXP.Information.Level; using GTFuckingXP.Managers; using GameData; using HarmonyLib; using Il2CppInterop.Runtime.Injection; using Il2CppSystem.Collections.Generic; using Microsoft.CodeAnalysis; using Player; 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; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("Expansions")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+gitc03396b-dirty-master")] [assembly: AssemblyProduct("Expansions")] [assembly: AssemblyTitle("Expansions")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace Expansions { [GeneratedCode("VersionInfoGenerator", "2.0.0+git50a4b1a-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 = "gitc03396b-dirty-master"; public const string SemVer = "1.0.0+gitc03396b-dirty-master"; public const string GitRevShort = "c03396b-dirty"; public const string GitRevLong = "c03396b43dd7f0dd7f6be64a74260c9045444f6f-dirty"; public const string GitBranch = "master"; public const string GitTag = null; public const bool GitIsDirty = true; } } namespace XpExpansions { [BepInPlugin("Endskill.XpExpansions", "XpExpansions", "1.0.0")] [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.0.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.Scripts { public class ClientSidedBioTrackerAbility : MonoBehaviour { private float _nextUpdate = 0f; public ClientSidedBioTrackerAbility(IntPtr intPtr) : base(intPtr) { } public void Update() { if (!(Time.time > _nextUpdate)) { return; } if (GameStateManager.IsInExpedition) { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); Enumerator<EnemyAgent> enumerator = AIG_CourseGraph.GetReachableEnemiesInNodes(((Agent)PlayerManager.GetLocalPlayerAgent()).CourseNode, 1).GetEnumerator(); while (enumerator.MoveNext()) { EnemyAgent current = enumerator.Current; ToolSyncManager.WantToTagEnemy(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 abstract void LevelReached(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(Level level) { LevelLayout levelLayout = CacheApiWrapper.GetCurrentLevelLayout(); List<LocalBioTrackerData> instance = CacheApi.GetInstance<List<LocalBioTrackerData>>(CacheApiWrapper.ExtensionCacheName); LocalBioTrackerData localBioTrackerData = instance.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)) { JsonSerializerOptions options = new JsonSerializerOptions { IncludeFields = false, ReadCommentHandling = JsonCommentHandling.Skip, PropertyNameCaseInsensitive = true, WriteIndented = true }; File.WriteAllText(path, JsonSerializer.Serialize(GetDefaultData(), options)); } } private void UpdateEverything() { CacheApi.SaveInstance<List<LocalBioTrackerData>>(JsonSerializer.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() { } public override void LevelReached(Level level) { } private void WriteDefaultJsonBlocks() { if (base.FolderPath.Contains("BepInEx")) { if (!Directory.Exists(base.FolderPath)) { Directory.CreateDirectory(base.FolderPath); } JsonSerializerOptions options = new JsonSerializerOptions { IncludeFields = false, ReadCommentHandling = JsonCommentHandling.Skip, PropertyNameCaseInsensitive = true, WriteIndented = true }; string path = Path.Combine(base.FolderPath, "DeactivateOnMissions.json"); if (!File.Exists(path)) { File.WriteAllText(path, JsonSerializer.Serialize(new List<int> { 69420, 42069 }, options)); } } } } public class DoubleJumpManager : BaseManager { public const string DoubleJumpXpExpansionId = "Endskill.DoubleJumpExpansion"; private const string _expansionFileName = "DoubleJumpExpansion.json"; private bool _harmonyState; public Harmony DoubleJumpHarmonyAbility { get; private set; } public DoubleJumpManager() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown DoubleJumpHarmonyAbility = new Harmony("Endskill.DoubleJumpExpansion"); Harmony.UnpatchID("com.mccad00.DoubleJump"); _harmonyState = false; } public override void Initialize() { WriteDefaultJsonBlocks(); UpdateEverything(); } public override void LevelCleanup() { if (_harmonyState) { DoubleJumpHarmonyAbility.UnpatchSelf(); _harmonyState = false; } } public override void LevelReached(Level level) { LogManager.Message("LevelReached, DoubleJump " + ((object)level).ToString()); LevelLayout levelLayout = CacheApiWrapper.GetCurrentLevelLayout(); List<DoubleJumpData> instance = CacheApi.GetInstance<List<DoubleJumpData>>(CacheApiWrapper.ExtensionCacheName); DoubleJumpData doubleJumpData = instance.FirstOrDefault((DoubleJumpData it) => it.LevelLayoutPersistentId == levelLayout.PersistentId); if (doubleJumpData != null) { if (doubleJumpData.UnlockAtLevel <= level.LevelNumber) { if (!_harmonyState) { Assembly assembly = Assembly.GetAssembly(typeof(EntryPoint)); DoubleJumpHarmonyAbility.PatchAll(assembly); _harmonyState = true; } } else if (_harmonyState) { DoubleJumpHarmonyAbility.UnpatchSelf(); _harmonyState = false; } } else if (_harmonyState) { DoubleJumpHarmonyAbility.UnpatchSelf(); _harmonyState = 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 },\r\n {\r\n \"LevelLayoutPersistentId\": 14,\r\n \"UnlockAtLevel\": 6\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 7,\r\n \"UnlockAtLevel\": 5\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 8,\r\n \"UnlockAtLevel\": 5\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 15,\r\n \"UnlockAtLevel\": 5\r\n }\r\n]"); } } private void UpdateEverything() { CacheApi.SaveInstance<List<DoubleJumpData>>(JsonSerializer.Deserialize<List<DoubleJumpData>>(File.ReadAllText(Path.Combine(base.FolderPath, "DoubleJumpExpansion.json"))), CacheApiWrapper.ExtensionCacheName); } private List<DoubleJumpData> GetDefaultData() { List<DoubleJumpData> list = new List<DoubleJumpData>(); for (int i = 0; i < 20; i++) { list.Add(new DoubleJumpData(i, 3)); } return list; } } public class ExpansionManager : BaseManager { public const string ExpansionActivePath = "ExpansionsActive.json"; public ExpansionManager() { LogManager.Message("ExpansionManager constructor."); InitApi.AddInitCallback((Action)Initialize); LevelApi.AddEndLevelCallback((Action)LevelCleanup); XpApi.AddOnLevelUpCallback((Action<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() { List<BaseManager> instance = CacheApi.GetInstance<List<BaseManager>>(CacheApiWrapper.ExtensionCacheName); foreach (BaseManager item in instance) { item.LevelCleanup(); } } public override void LevelReached(Level level) { LogManager.Message("LevelReached in the Expansion Manager."); List<BaseManager> instance = CacheApi.GetInstance<List<BaseManager>>(CacheApiWrapper.ExtensionCacheName); foreach (BaseManager item in instance) { item.LevelReached(level); } } public override void LevelInitialized(Level level) { LogManager.Message("LevelInitialized in the Expansion Manager."); List<BaseManager> instance = CacheApi.GetInstance<List<BaseManager>>(CacheApiWrapper.ExtensionCacheName); foreach (BaseManager item in instance) { item.LevelInitialized(level); } } private List<BaseManager> CreateManagers() { List<BaseManager> list = new List<BaseManager>(); string path = Path.Combine(base.FolderPath, "ExpansionsActive.json"); ActiveExpansions activeExpansions = JsonSerializer.Deserialize<ActiveExpansions>(File.ReadAllText(path)); 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()); } } 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, JsonSerializer.Serialize(new ActiveExpansions(startingXp: false, doubleJumpAbility: true))); } } } public class ExplosionAbilityManager : BaseManager { public const string ExplosionHarmonyInstanceId = "Endskill.ExplosionAbility"; public Harmony ExplosionHarmony { get; private set; } public ExplosionAbilityManager() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown ExplosionHarmony = new Harmony("Endskill.ExplosionAbility"); } public override void Initialize() { } public override void LevelReached(Level level) { } 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); } } internal class StartingLevelXpManager : BaseManager { private const string _expansionFileName = "StartingXP.json"; public override void Initialize() { WriteDefaultJsonBlocks(); UpdateEverything(); } public override void LevelReached(Level level) { } public override void LevelInitialized(Level level) { ExpeditionInTierData expedition = RundownManager.ActiveExpedition; List<StartingXpData> instance = CacheApi.GetInstance<List<StartingXpData>>(CacheApiWrapper.ExtensionCacheName); StartingXpData startingXpData = instance.FirstOrDefault((StartingXpData it) => it.LevelLayoutData == expedition.LevelLayoutData); if (startingXpData != null) { XpApi.AddXp(startingXpData.StartingXp); } } private void UpdateEverything() { CacheApi.SaveInstance<List<StartingXpData>>(JsonSerializer.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)) { JsonSerializerOptions options = new JsonSerializerOptions { IncludeFields = false, ReadCommentHandling = JsonCommentHandling.Skip, PropertyNameCaseInsensitive = true, WriteIndented = true }; File.WriteAllText(path, JsonSerializer.Serialize(GetDefaultData(), options)); } } 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 current2 = enumerator2.Current; list.Add(new StartingXpData(current2.LevelLayoutData, 100u)); } } return list; } } } namespace XpExpansions.Information { public class ActiveExpansions { public bool DoubleJumpAbility { get; set; } public bool StartingXp { get; set; } public bool LivingBioAbility { get; set; } public ActiveExpansions(bool startingXp, bool doubleJumpAbility) { DoubleJumpAbility = doubleJumpAbility; StartingXp = startingXp; } } } namespace XpExpansions.Information.StartingXp { public class StartingXpData { public uint LevelLayoutData { get; set; } public uint StartingXp { get; set; } public StartingXpData(uint levelLayoutData, uint 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 { internal class DoubleJumpData { public int LevelLayoutPersistentId { get; set; } public int UnlockAtLevel { get; set; } public DoubleJumpData(int levelLayoutPersistentId, int unlockAtLevel) { LevelLayoutPersistentId = levelLayoutPersistentId; UnlockAtLevel = unlockAtLevel; } } } 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 weeks 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.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; 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 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 ExtraSyringeCustomization.Utils; using FloatingTextAPI; using GTFO.API; using GTFuckingXP.Communication; using GTFuckingXP.Enums; using GTFuckingXP.Extensions; 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.StolenCode; using GTFuckingXp.Managers; using GTFuckingXp.Patches; using GameData; using Gear; using HarmonyLib; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Localization; using Microsoft.CodeAnalysis; 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+git157d3f1-dirty-dinorush-changes.157d3f1160e83ea0a6e4faa5f1ffe11a18bd85e2")] [assembly: AssemblyProduct("GTFuckingXP")] [assembly: AssemblyTitle("GTFuckingXP")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } 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(typeof(Dam_SyncedDamageBase))] internal static class PlayerDamagePatches { [HarmonyPatch("FireDamage")] [HarmonyPrefix] private static void Prefix_FireDamage(Dam_SyncedDamageBase __instance, ref float dam, Agent sourceAgent) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)__instance.DamageBaseOwner != 0) { return; } PlayerAgent val = ((Il2CppObjectBase)__instance.GetBaseAgent()).Cast<PlayerAgent>(); if (((Agent)val).Alive && ((Agent)val).IsLocallyOwned) { CustomScalingBuff customScalingBuff = CacheApiWrapper.GetActiveLevel().CustomScaling.FirstOrDefault((CustomScalingBuff buff) => buff.CustomBuff == CustomScaling.BleedResistance); if (customScalingBuff != null) { dam *= 2f - customScalingBuff.Value; } } } [HarmonyPatch("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) { PlayerAgent val = ((Il2CppObjectBase)__instance.GetBaseAgent()).Cast<PlayerAgent>(); Level value; if (((Agent)val).IsLocallyOwned) { value = CacheApiWrapper.GetActiveLevel(); } else if (!CacheApiWrapper.GetPlayerToLevelMapping().TryGetValue(val.PlayerSlotIndex, out value)) { return; } CustomScalingBuff customScalingBuff = value.CustomScaling.FirstOrDefault((CustomScalingBuff buff) => buff.CustomBuff == CustomScaling.ExplosionResistance); if (customScalingBuff != null) { dam *= 2f - customScalingBuff.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_014d: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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.LobbyPlayers.GetEnumerator(); XpHandler xpHandler = default(XpHandler); while (enumerator.MoveNext()) { SNet_Player current = enumerator.Current; if (current.IsBot) { continue; } if (info.LastHitDealtBy.Owner.PlayerSlot.index == current.PlayerSlot.index) { if (current.IsLocal) { if (CacheApi.TryGetInstance<XpHandler>(ref xpHandler, "GTF_XP", true)) { xpHandler.AddXp(enemyXp, position); } } else { NetworkApiXpManager.SendReceiveXp(current, enemyXp, position, forceDebuffXp: false); } continue; } float damageDealtBySnet = info.GetDamageDealtBySnet(current); if (damageDealtBySnet > 0.5f) { float num = damageDealtBySnet / ((Dam_SyncedDamageBase)info.KilledEnemyAgent.Damage).HealthMax; LogManager.Debug($"percentageDealt = {num} and damageDealt is {damageDealtBySnet}"); NetworkApiXpManager.SendStaticXpInfo(current, (uint)((float)enemyXp.XpGain * num), (uint)((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) { EnemyAgent killedEnemy2 = killedEnemy; List<EnemyXp> instance = CacheApi.GetInstance<List<EnemyXp>>("GTF_XP"); EnemyXp enemyXp = instance.FirstOrDefault((EnemyXp it) => it.EnemyId == killedEnemy2.EnemyDataID); if (enemyXp == null) { LogManager.Warn($"There was no enemy XP data found for {killedEnemy2.EnemyDataID}!"); enemyXp = new EnemyXp(killedEnemy2.EnemyDataID, ((Object)killedEnemy2).name, 0u, 0u, 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 },\r\n {\r\n \"LevelLayoutPersistentId\": 14,\r\n \"UnlockAtLevel\": 6\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 7,\r\n \"UnlockAtLevel\": 5\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 8,\r\n \"UnlockAtLevel\": 5\r\n },\r\n {\r\n \"LevelLayoutPersistentId\": 15,\r\n \"UnlockAtLevel\": 5\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 \"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 \"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 \"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 \"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 \"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 \"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 \"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 \"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 \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee Armor +2%\\nProjectile Armor +5%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 118% -> 120%\\nProjectile Armor 145% -> 150%\\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\": 10,\r\n \"TotalXpRequired\": 6850, \r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee Armor +2%\\nProjectile Armor +5%\\n20% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 120% -> 122%\\nProjectile Armor 150% -> 155%\\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\": 11,\r\n \"TotalXpRequired\": 7950,\r\n \"CustomLevelUpPopupText\": \"Lv Up!\\nMelee Armor +2%\\nProjectile Armor +5%\\n10% Heal\",\r\n \"CustomLevelStatsText\": \"Class: Outlander\\n{0}\\nMelee Armor 122% -> 124%\\nProjectile Armor 155% -> 160%\\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