using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using AK;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using BepInEx.Unity.IL2CPP.Hook;
using CellMenu;
using ChainedPuzzles;
using GTFO.API;
using GameData;
using GameEvent;
using Gear;
using Globals;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppInterop.Runtime.InteropTypes.Fields;
using Il2CppInterop.Runtime.Runtime;
using Il2CppInterop.Runtime.Runtime.VersionSpecific.Class;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using MTFO.API;
using MTFO.Custom;
using MTFO.Custom.CCP.Components;
using MTFO.CustomCP;
using MTFO.HotReload;
using MTFO.Managers;
using MTFO.NativeDetours;
using MTFO.Utilities;
using Player;
using SNetwork;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.Analytics;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("MTFO")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("© dakkhuza 2021")]
[assembly: AssemblyFileVersion("4.6.2")]
[assembly: AssemblyInformationalVersion("4.6.2+git44e73d7-dirty-main")]
[assembly: AssemblyProduct("MTFO")]
[assembly: AssemblyTitle("MTFO")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("4.6.2.0")]
[module: UnverifiableCode]
namespace MTFO
{
[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[DebuggerNonUserCode]
[CompilerGenerated]
internal class ConfigStrings
{
private static ResourceManager resourceMan;
private static CultureInfo resourceCulture;
[EditorBrowsable(EditorBrowsableState.Advanced)]
internal static ResourceManager ResourceManager
{
get
{
if (resourceMan == null)
{
resourceMan = new ResourceManager("MTFO.ConfigStrings", typeof(ConfigStrings).Assembly);
}
return resourceMan;
}
}
[EditorBrowsable(EditorBrowsableState.Advanced)]
internal static CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
internal static string SECTION_DEBUG => ResourceManager.GetString("SECTION_DEBUG", resourceCulture);
internal static string SECTION_DEV => ResourceManager.GetString("SECTION_DEV", resourceCulture);
internal static string SECTION_GENERAL => ResourceManager.GetString("SECTION_GENERAL", resourceCulture);
internal static string SETTING_DISABLE_ACHIEVEMENTS => ResourceManager.GetString("SETTING_DISABLE_ACHIEVEMENTS", resourceCulture);
internal static string SETTING_DISABLE_ACHIEVEMENTS_DESC => ResourceManager.GetString("SETTING_DISABLE_ACHIEVEMENTS_DESC", resourceCulture);
internal static string SETTING_DUMPDATA => ResourceManager.GetString("SETTING_DUMPDATA", resourceCulture);
internal static string SETTING_DUMPDATA_DESC => ResourceManager.GetString("SETTING_DUMPDATA_DESC", resourceCulture);
internal static string SETTING_DUMPDATA_MODE => ResourceManager.GetString("SETTING_DUMPDATA_MODE", resourceCulture);
internal static string SETTING_DUMPDATA_MODE_DESC => ResourceManager.GetString("SETTING_DUMPDATA_MODE_DESC", resourceCulture);
internal static string SETTING_HOTRELOAD => ResourceManager.GetString("SETTING_HOTRELOAD", resourceCulture);
internal static string SETTING_HOTRELOAD_DESC => ResourceManager.GetString("SETTING_HOTRELOAD_DESC", resourceCulture);
internal static string SETTING_RUNDOWNPACKAGE => ResourceManager.GetString("SETTING_RUNDOWNPACKAGE", resourceCulture);
internal static string SETTING_RUNDOWNPACKAGE_DESC => ResourceManager.GetString("SETTING_RUNDOWNPACKAGE_DESC", resourceCulture);
internal static string SETTING_USE_LEGACY_PATH => ResourceManager.GetString("SETTING_USE_LEGACY_PATH", resourceCulture);
internal static string SETTING_USE_LEGACY_PATH_DESC => ResourceManager.GetString("SETTING_USE_LEGACY_PATH_DESC", resourceCulture);
internal static string SETTING_VERBOSE => ResourceManager.GetString("SETTING_VERBOSE", resourceCulture);
internal static string SETTING_VERBOSE_DESC => ResourceManager.GetString("SETTING_VERBOSE_DESC", resourceCulture);
internal ConfigStrings()
{
}
}
[BepInPlugin("com.dak.MTFO", "MTFO", "4.6.2+git44e73d7-dirty-main")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class MTFO : BasePlugin
{
public const string MODNAME = "MTFO";
public const string AUTHOR = "dak";
public const string GUID = "com.dak.MTFO";
public const string VERSION = "4.6.2+git44e73d7-dirty-main";
public override void Load()
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Expected O, but got Unknown
Analytics.enabled = false;
Harmony harmony = new Harmony("com.dak.MTFO");
AssetAPI.OnImplReady += delegate
{
if (ConfigManager.IsHotReloadEnabled)
{
ClassInjector.RegisterTypeInIl2Cpp<HotReloader>();
harmony.PatchAll(typeof(HotReloadInjector));
}
};
harmony.PatchAll();
Detour_DataBlockBase.Patch();
}
}
public static class DataDumperExtensions
{
public static int GetStableHashCode(this string str)
{
int num = 5381;
int num2 = num;
for (int i = 0; i < str.Length && str[i] != 0; i += 2)
{
num = ((num << 5) + num) ^ str[i];
if (i == str.Length - 1 || str[i + 1] == '\0')
{
break;
}
num2 = ((num2 << 5) + num2) ^ str[i + 1];
}
return num + num2 * 1566083941;
}
}
public static class JsonNodeExtension
{
private static JsonDocumentOptions _JsonDocumentOptions = new JsonDocumentOptions
{
AllowTrailingCommas = true,
CommentHandling = JsonCommentHandling.Skip
};
private static readonly JsonSerializerOptions _JsonSerializerOptions = new JsonSerializerOptions
{
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip,
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
public static string ToJsonStringIndented(this JsonNode jsonNode)
{
return jsonNode.ToJsonString(_JsonSerializerOptions);
}
public static bool TryParseToJsonNode(this string json, out JsonNode jsonNode)
{
try
{
jsonNode = json.ToJsonNode();
return jsonNode != null;
}
catch
{
jsonNode = null;
return false;
}
}
public static bool TryParseJsonNode(this Stream stream, bool dispose, out JsonNode jsonNode)
{
try
{
jsonNode = stream.ToJsonNode();
return jsonNode != null;
}
catch
{
jsonNode = null;
return false;
}
finally
{
if (dispose)
{
stream.Close();
}
}
}
public static JsonNode ToJsonNode(this string json)
{
return JsonNode.Parse(json, null, _JsonDocumentOptions);
}
public static JsonNode ToJsonNode(this Stream jsonStream)
{
return JsonNode.Parse(jsonStream, null, _JsonDocumentOptions);
}
public static bool TryAddJsonItem(this JsonArray jsonArray, string json)
{
try
{
if (jsonArray == null)
{
throw new Exception();
}
if (string.IsNullOrWhiteSpace(json))
{
throw new Exception();
}
if (!json.TryParseToJsonNode(out var jsonNode))
{
throw new Exception();
}
jsonArray.Add(jsonNode);
return true;
}
catch
{
return false;
}
}
}
[GeneratedCode("VersionInfoGenerator", "2.0.0+git50a4b1a-master")]
[CompilerGenerated]
internal static class VersionInfo
{
public const string RootNamespace = "MTFO";
public const string Version = "4.6.2";
public const string VersionPrerelease = null;
public const string VersionMetadata = "git44e73d7-dirty-main";
public const string SemVer = "4.6.2+git44e73d7-dirty-main";
public const string GitRevShort = "44e73d7-dirty";
public const string GitRevLong = "44e73d7509b98440c382e03ed0d3eef361127d4f-dirty";
public const string GitBranch = "main";
public const string GitTag = "v4.3.0";
public const bool GitIsDirty = true;
}
}
namespace MTFO.Utilities
{
public class JsonSerializer
{
public JsonSerializerOptions Options;
public JsonSerializer()
{
Options = new JsonSerializerOptions
{
AllowTrailingCommas = true,
IncludeFields = true,
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip,
WriteIndented = true
};
Options.Converters.Add(new JsonStringEnumConverter());
}
public JsonSerializer(bool allowTrailingCommas = true, bool includeFields = true, bool propertyNameCaseInsensitive = true, bool writeIndented = true)
{
Options = new JsonSerializerOptions
{
AllowTrailingCommas = allowTrailingCommas,
IncludeFields = includeFields,
PropertyNameCaseInsensitive = propertyNameCaseInsensitive,
ReadCommentHandling = JsonCommentHandling.Skip,
WriteIndented = writeIndented
};
Options.Converters.Add(new JsonStringEnumConverter());
}
public void TryRead<T>(string path, out T output) where T : new()
{
if (File.Exists(path))
{
string json = File.ReadAllText(path);
output = Deserialize<T>(json);
}
else
{
output = new T();
string json = Serialize(output);
File.WriteAllText(path, json);
}
}
public string Serialize(object value)
{
return System.Text.Json.JsonSerializer.Serialize(value, Options);
}
public T Deserialize<T>(string json)
{
return System.Text.Json.JsonSerializer.Deserialize<T>(json, Options);
}
}
public static class Log
{
private static readonly ManualLogSource logger;
static Log()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Expected O, but got Unknown
logger = new ManualLogSource("MTFO");
Logger.Sources.Add((ILogSource)(object)logger);
}
public static void Verbose(object msg)
{
if (ConfigManager.IsVerbose)
{
logger.LogInfo(msg);
}
}
public static void Debug(object msg)
{
logger.LogDebug(msg);
}
public static void Message(object msg)
{
logger.LogMessage(msg);
}
public static void Error(object msg)
{
logger.LogError(msg);
}
public static void Warn(object msg)
{
logger.LogWarning(msg);
}
}
public enum BaseDirectory
{
BepInEx,
Plugins,
GameData
}
public static class PathUtil
{
public static string Prepare(BaseDirectory baseDir, params string[] subDirs)
{
string text = baseDir switch
{
BaseDirectory.BepInEx => Paths.BepInExRootPath,
BaseDirectory.Plugins => Paths.PluginPath,
BaseDirectory.GameData => Path.Combine(Paths.BepInExRootPath, "GameData"),
_ => throw new ArgumentOutOfRangeException("baseDir", string.Format("{0} is not a valid value for Argument: {1}", baseDir, "baseDir")),
};
string obj = ((subDirs == null || subDirs.Length == 0) ? text : Path.Combine(text, Path.Combine(subDirs)));
Directory.CreateDirectory(obj);
return obj;
}
public static bool CheckCustomFile(string file, out string CombinedPath)
{
CombinedPath = Path.Combine(ConfigManager.CustomPath, file);
if (File.Exists(CombinedPath))
{
return true;
}
return false;
}
public static bool CheckFile(string pathToFile)
{
if (File.Exists(pathToFile))
{
return true;
}
return false;
}
[Obsolete]
public static string MakeRelativeDirectory(string path, bool createPath = true)
{
string text = Path.Combine(Path.Combine(Paths.ConfigPath, "Rundowns"), path);
if (createPath && !Directory.Exists(text))
{
Directory.CreateDirectory(text);
}
return text;
}
public static string MakeRelativeDirectory(string path, string folder)
{
string text = Path.Combine(path, folder);
if (!Directory.Exists(text))
{
Directory.CreateDirectory(text);
}
return text;
}
public static void PrepareEmptyDirectory(string path)
{
if (Directory.Exists(path))
{
Directory.Delete(path, recursive: true);
}
Directory.CreateDirectory(path);
}
public static Stream OpenUtf8Stream(string filePath)
{
return new StreamReader(filePath, Encoding.UTF8).BaseStream;
}
}
}
namespace MTFO.Patches
{
[HarmonyPatch(typeof(SteamUserStats), "SetAchievement")]
internal static class Patch_SteamAPI_Achievement
{
private static bool Prefix(string pchName)
{
if (!ConfigManager.DisableAchievements)
{
return true;
}
Log.Error("Achievement Completion Blocked: " + pchName);
return false;
}
}
[HarmonyPatch(typeof(AnalyticsManager), "OnGameEvent", new Type[] { typeof(GameEventData) })]
internal static class Patch_Analytics
{
public static bool Prefix(GameEventData data)
{
return false;
}
}
[HarmonyPatch(typeof(SNet_Core_STEAM), "SetFriendsData", new Type[]
{
typeof(FriendsDataType),
typeof(string)
})]
internal static class Patch_RichPresence2
{
public static void Prefix(FriendsDataType type, ref string data, SNet_Core_STEAM __instance)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Invalid comparison between Unknown and I4
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
if ((int)type != 1)
{
if ((int)type == 9)
{
data = "0";
}
else
{
Log.Verbose($"Lobby data\nType: {type} Data: {data}");
}
}
else
{
data = "MODDED - " + data;
}
}
}
[HarmonyPatch(typeof(DiscordManager), "UpdateDiscordDetails")]
internal static class Patch_DiscordManager
{
[HarmonyPostfix]
private static void Post_UpdateDiscordDetails(eDiscordDetailsDisplay details)
{
}
}
[HarmonyPatch(typeof(GlowstickInstance), "Update")]
internal static class Patch_Glowstick
{
public static void Postfix(GlowstickInstance __instance)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Invalid comparison between Unknown and I4
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Invalid comparison between Unknown and I4
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
if (ConfigManager.HasCustomContent && ConfigManager.CustomContent.GlowstickHolder != null && !((Object)(object)__instance == (Object)null) && (int)__instance.m_state != 3 && (int)__instance.m_state != 4 && ConfigManager.CustomContent.GlowstickHolder.GlowstickLookup.TryGetValue(((Item)__instance).PublicName, out var value) && (Object)(object)__instance.m_light != (Object)null)
{
((LightBase)__instance.m_light).Range = value.Range;
((LightBase)__instance.m_light).Color = value.Color * __instance.m_progression;
((LightBase)__instance.m_light).UpdateVisibility(true);
}
}
}
[HarmonyPatch(typeof(CP_Holopath_Spline), "Reveal")]
internal static class Patch_InstantRevealPuzzlePath
{
public static bool Prefix(CP_Holopath_Spline __instance)
{
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
if (__instance.m_revealSpeed < 0f)
{
if (!__instance.m_isSetup)
{
return false;
}
__instance.SetSplineProgress(1f);
__instance.SetVisible(true);
__instance.m_sound.UpdatePosition(__instance.TipPos);
if (!__instance.m_didSetColor)
{
__instance.m_didSetColor = __instance.TrySetColor();
}
__instance.m_sound.Post(EVENTS.BIOSCAN_TUBE_EMITTER_STOP, true);
Action onRevealDone = __instance.OnRevealDone;
if (onRevealDone != null)
{
onRevealDone.Invoke();
}
return false;
}
return true;
}
}
[HarmonyPatch(typeof(CM_PageLoadout), "UpdatePageData")]
internal static class Patch_PageLoadoutUpdatePageData
{
public static void Prefix(CM_PageLoadout __instance)
{
((TMP_Text)((Component)((Transform)((CM_PageBase)__instance).m_movingContentHolder).Find("ShareServerId/ShareText")).gameObject.GetComponent<TextMeshPro>()).SetText("<color=red><b>Do not play modded content on the official GTFO server or online matchmake lobbies.</b></color>\n\nFeel free to join the unofficial discord server linked below and ask people to play.", true);
__instance.m_discordButton.SetText("Mod Server");
__instance.m_discordButton.OnBtnPressCallback = Action<int>.op_Implicit((Action<int>)delegate
{
Application.OpenURL("https://discord.com/invite/rRMPtv4FAh");
});
}
}
[HarmonyPatch(typeof(CM_PageLoadout), "UpdatePlayerBars")]
internal static class Patch_PageLoadoutUpdatePlayerBars
{
public static void Prefix(CM_PageLoadout __instance)
{
foreach (CM_PlayerLobbyBar item in (Il2CppArrayBase<CM_PlayerLobbyBar>)(object)__instance.m_playerLobbyBars)
{
item.m_matchmakeButton.SetOnBtnPressCallback((Action<int>)null);
item.m_matchmakeButton.SetText("DISABLED");
}
}
}
[HarmonyPatch(typeof(CM_PageRundown_New), "Intro_RevealRundown")]
internal static class Patch_PageRundownNew
{
public static void Postfix(CM_PageRundown_New __instance)
{
__instance.m_discordButton.SetText("MOD SERVER");
__instance.m_discordButton.OnBtnPressCallback = Action<int>.op_Implicit((Action<int>)delegate
{
Application.OpenURL("https://discord.com/invite/rRMPtv4FAh");
});
((RectTransformComp)__instance.m_matchmakeAllButton).SetVisible(false);
__instance.m_matchmakeAllButton.SetText("MATCHMAKE DISABLED");
__instance.m_matchmakeAllButton.OnBtnPressCallback = null;
__instance.m_aboutTheRundownButton.SetText("THUNDERSTORE");
__instance.m_aboutTheRundownButton.OnBtnPressCallback = Action<int>.op_Implicit((Action<int>)delegate
{
Application.OpenURL("https://gtfo.thunderstore.io/");
});
}
}
[HarmonyPatch(typeof(CM_PageRundown_New), "PlaceRundown")]
internal static class Patch_RundownTierMarker
{
public static void Postfix(CM_PageRundown_New __instance)
{
ContentManager customContent = ConfigManager.CustomContent;
if (customContent != null)
{
__instance.m_tierMarker1.m_name = customContent.TierNames.Tier1;
__instance.m_tierMarker2.m_name = customContent.TierNames.Tier2;
__instance.m_tierMarker3.m_name = customContent.TierNames.Tier3;
__instance.m_tierMarker4.m_name = customContent.TierNames.Tier4;
__instance.m_tierMarker5.m_name = customContent.TierNames.Tier5;
__instance.m_tierMarker1.UpdateHeader();
__instance.m_tierMarker2.UpdateHeader();
__instance.m_tierMarker3.UpdateHeader();
__instance.m_tierMarker4.UpdateHeader();
__instance.m_tierMarker5.UpdateHeader();
}
}
}
[HarmonyPatch(typeof(PUI_Watermark), "UpdateWatermark")]
internal static class Patch_WatermarkUpdateWatermark
{
public static void Postfix(PUI_Watermark __instance)
{
((TMP_Text)__instance.m_watermarkText).SetText("<color=red>MODDED</color> <color=orange>4.6.2</color>", true);
}
}
}
namespace MTFO.NativeDetours
{
internal class DataBlockBaseWrapper
{
private static readonly HashSet<string> _PreferPartialDumpBlocks = new HashSet<string>
{
"RundownDataBlock".ToLower(),
"LightSettingsDataBlock".ToLower(),
"FogSettingsDataBlock".ToLower(),
"LevelLayoutDataBlock".ToLower(),
"WardenObjectiveDataBlock".ToLower(),
"DimensionDataBlock".ToLower(),
"EnemyDataBlock".ToLower(),
"EnemySFXDataBlock".ToLower(),
"EnemyBehaviorDataBlock".ToLower(),
"EnemyBalancingDataBlock".ToLower(),
"EnemyMovementDataBlock".ToLower(),
"ArchetypeDataBlock".ToLower(),
"PlayerOfflineGearDataBlock".ToLower(),
"ComplexResourceSetDataBlock".ToLower(),
"TextDataBlock".ToLower()
};
private IntPtr Ptr__m_fileNameNoExt;
public IntPtr ClassPointer { get; private set; }
public string FileName { get; private set; }
public string BinaryFileName { get; private set; }
public bool PreferPartialBlockOnDump { get; private set; }
public unsafe DataBlockBaseWrapper(Il2CppMethodInfo* methodInfo)
{
INativeClassStruct val = UnityVersionHandler.Wrap(UnityVersionHandler.Wrap(methodInfo).Class);
ClassPointer = ((INativeStruct)val).Pointer;
Ptr__m_fileNameNoExt = IL2CPP.GetIl2CppField(((INativeStruct)val).Pointer, "m_fileNameNoExt");
IntPtr zero = IntPtr.Zero;
IL2CPP.il2cpp_field_static_get_value(Ptr__m_fileNameNoExt, (void*)(&zero));
FileName = IL2CPP.Il2CppStringToManaged(zero).Replace('.', '_');
BinaryFileName = FileName + "_bin";
if (_PreferPartialDumpBlocks.Contains(FileName.ToLower().Replace("gamedata_", "")))
{
PreferPartialBlockOnDump = true;
}
}
}
internal static class Detour_DataBlockBase
{
private unsafe delegate IntPtr GetFileContentsDel(Il2CppMethodInfo* methodInfo);
private static string _BasePathToDump;
private static INativeDetour _Detour;
private static GetFileContentsDel _Original;
public unsafe static void Patch()
{
_BasePathToDump = Path.Combine(Paths.BepInExRootPath, "GameData-Dump", CellBuildData.GetRevision().ToString());
if (ConfigManager.DumpGameData)
{
PathUtil.PrepareEmptyDirectory(_BasePathToDump);
}
_Detour = INativeDetour.CreateAndApply<GetFileContentsDel>((IntPtr)(nint)Il2CppAPI.GetIl2CppMethod<GameDataBlockBase<EnemyDataBlock>>("GetFileContents", typeof(string).FullName, false, Array.Empty<string>()), (GetFileContentsDel)Dtor_GetFileContents, ref _Original);
}
private unsafe static IntPtr Dtor_GetFileContents(Il2CppMethodInfo* methodInfo)
{
IntPtr intPtr = _Original(methodInfo);
try
{
DataBlockBaseWrapper dataBlockBaseWrapper = new DataBlockBaseWrapper(methodInfo);
string binaryFileName = dataBlockBaseWrapper.BinaryFileName;
_ = binaryFileName + ".json";
if (ConfigManager.DumpGameData)
{
DumpContent(dataBlockBaseWrapper, IL2CPP.Il2CppStringToManaged(intPtr));
}
Log.Verbose("GetFileContents Call of " + binaryFileName);
Stream contentStream = GetContentStream(dataBlockBaseWrapper, intPtr);
JsonNode jsonNode = contentStream.ToJsonNode();
JsonArray jsonArray = jsonNode["Blocks"].AsArray();
string path = Path.Combine(ConfigManager.GameDataPath, dataBlockBaseWrapper.FileName);
if (Directory.Exists(path))
{
int num = 0;
string[] files = Directory.GetFiles(path, "*.json");
foreach (string text in files)
{
Log.Verbose(" - Trying to add PartialData [" + text + "]");
if (File.Exists(text) && PathUtil.OpenUtf8Stream(text).TryParseJsonNode(dispose: true, out var jsonNode2))
{
jsonArray.Add(jsonNode2);
num++;
}
}
Log.Verbose($" - Added {num} partial data of {binaryFileName}");
}
List<string> jsonItemsToInject = new List<string>();
MTFOGameDataAPI.Invoke_OnGameDataContentLoad(dataBlockBaseWrapper.FileName, contentStream, in jsonItemsToInject);
foreach (string item2 in jsonItemsToInject)
{
try
{
JsonNode item = item2.ToJsonNode();
jsonArray.Add(item);
}
catch (Exception ex)
{
Log.Error("Exception were found while reading Injected Json Data!");
Log.Error(ex.ToString());
Log.Error("Full Json:\n" + item2);
}
}
uint num2 = 0u;
foreach (JsonNode item3 in jsonNode["Blocks"].AsArray())
{
uint num3 = (uint)(JsonNode)item3["persistentID"].AsValue();
if (num3 > num2)
{
num2 = num3;
}
}
jsonNode["LastPersistentID"] = num2;
string text2 = jsonNode.ToJsonStringIndented();
IntPtr result = IL2CPP.ManagedStringToIl2Cpp(text2);
MTFOGameDataAPI.Invoke_OnGameDataContentLoaded(dataBlockBaseWrapper.FileName, text2);
return result;
}
catch (Exception ex2)
{
Log.Error("Exception were found while handling Detour;Falling back to original content!");
Log.Error(ex2.ToString());
return intPtr;
}
}
private static Stream GetContentStream(DataBlockBaseWrapper datablock, IntPtr originalContentPtr)
{
string binaryFileName = datablock.BinaryFileName;
string path = binaryFileName + ".json";
string s = IL2CPP.Il2CppStringToManaged(originalContentPtr);
string text = Path.Combine(ConfigManager.GameDataPath, path);
if (File.Exists(text))
{
Log.Verbose("Opening filestream of [" + binaryFileName + "] from disk...");
Log.Verbose(text);
return PathUtil.OpenUtf8Stream(text);
}
Log.Verbose("No file found at [" + binaryFileName + "]");
return new MemoryStream(Encoding.UTF8.GetBytes(s));
}
private static void DumpContent(DataBlockBaseWrapper datablock, string json)
{
if (!json.TryParseToJsonNode(out var jsonNode))
{
Log.Verbose("Unable to dump " + datablock.FileName + ", Invalid Json Content!");
return;
}
bool flag = false;
switch (ConfigManager.DumpMode)
{
case DumpGameDataMode.Single:
flag = false;
break;
case DumpGameDataMode.PartialData:
flag = datablock.PreferPartialBlockOnDump;
break;
case DumpGameDataMode.FullPartialData:
flag = true;
break;
}
if (flag)
{
JsonArray jsonArray = jsonNode["Blocks"].AsArray();
string text = Path.Combine(path2: datablock.FileName, path1: _BasePathToDump);
PathUtil.PrepareEmptyDirectory(text);
foreach (JsonNode item in jsonArray)
{
string path = string.Format("{0}__{1}.json", item["persistentID"], item["name"]);
string text2 = Path.Combine(text, path);
File.WriteAllText(text2, item.ToJsonStringIndented());
Log.Verbose(" - Save... " + text2);
}
jsonNode["Blocks"] = new JsonArray();
}
string path2 = datablock.BinaryFileName + ".json";
File.WriteAllText(Path.Combine(_BasePathToDump, path2), jsonNode.ToJsonStringIndented());
Log.Verbose(datablock.FileName + " has dumped to '" + _BasePathToDump + "'");
}
}
}
namespace MTFO.Managers
{
public enum DumpGameDataMode
{
Single,
PartialData,
FullPartialData
}
public static class ConfigManager
{
private const string CUSTOM_FOLDER = "Custom";
private static readonly ConfigEntry<bool> _enableHotReload;
private static readonly ConfigEntry<bool> _dumpGameData;
private static readonly ConfigEntry<bool> _isVerbose;
private static readonly ConfigEntry<DumpGameDataMode> _dumpGameDataMode;
private static readonly ConfigEntry<bool> _disableAchievements;
public static int GAME_VERSION;
public static ContentManager CustomContent;
public static readonly string GameDataPath;
public static readonly string CustomPath;
public static bool HasGameDataPath;
public static bool HasCustomContent;
public static bool IsModded;
public static bool IsPluginGameDataPath;
public static bool IsVerbose => _isVerbose.Value;
public static bool DisableAchievements => _disableAchievements.Value;
public static bool IsHotReloadEnabled => _enableHotReload.Value;
public static bool DumpGameData => _dumpGameData.Value;
public static DumpGameDataMode DumpMode => _dumpGameDataMode.Value;
private static string ResolveGameDataPath(string rootPath)
{
foreach (string item in Directory.GetFiles(rootPath, "GameData_*.json", SearchOption.AllDirectories).OrderBy(Path.GetDirectoryName))
{
if (Path.GetDirectoryName(item) != GameDataPath)
{
HasGameDataPath = true;
return Path.GetDirectoryName(item);
}
}
return null;
}
static ConfigManager()
{
//IL_0010: 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_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
ConfigFile val = new ConfigFile(Path.Combine(Paths.ConfigPath, "MTFO.cfg"), true);
_enableHotReload = val.Bind<bool>(ConfigStrings.SECTION_DEV, ConfigStrings.SETTING_HOTRELOAD, false, ConfigStrings.SETTING_HOTRELOAD_DESC);
_dumpGameData = val.Bind<bool>(ConfigStrings.SECTION_DEV, ConfigStrings.SETTING_DUMPDATA, false, ConfigStrings.SETTING_DUMPDATA_DESC);
_dumpGameDataMode = val.Bind<DumpGameDataMode>(ConfigStrings.SECTION_DEV, ConfigStrings.SETTING_DUMPDATA_MODE, DumpGameDataMode.Single, ConfigStrings.SETTING_DUMPDATA_MODE_DESC);
_isVerbose = val.Bind<bool>(ConfigStrings.SECTION_DEBUG, ConfigStrings.SETTING_VERBOSE, false, ConfigStrings.SETTING_VERBOSE_DESC);
_disableAchievements = val.Bind<bool>(ConfigStrings.SECTION_GENERAL, ConfigStrings.SETTING_DISABLE_ACHIEVEMENTS, true, ConfigStrings.SETTING_DISABLE_ACHIEVEMENTS_DESC);
GAME_VERSION = GetGameVersion();
GameDataPath = ResolveGameDataPath(PathUtil.Prepare(BaseDirectory.GameData));
IsPluginGameDataPath = string.IsNullOrEmpty(GameDataPath);
if (IsPluginGameDataPath)
{
GameDataPath = ResolveGameDataPath(Paths.PluginPath);
Log.Warn("Plugin paths for gamedata are under legacy support and will be removed in the future. Considering migrating to the '\\BepInEx\\GameData' folder.");
}
if (HasGameDataPath)
{
CustomPath = Path.Combine(GameDataPath, "Custom");
HasCustomContent = Directory.Exists(CustomPath);
}
else
{
GameDataPath = string.Empty;
CustomPath = string.Empty;
HasCustomContent = false;
IsPluginGameDataPath = false;
}
try
{
CustomContent = new ContentManager();
}
catch (Exception arg)
{
HasCustomContent = false;
Log.Error($"Failed to init custom content!\nIs your JSON valid?\n---- ERROR MESSAGE ---- {arg} ---- END ERROR MESSAGE ----");
}
Log.Debug("---- DEBUG INFO ----");
Log.Debug($"Time: {DateTime.Now}");
Log.Debug($"Game Version: {GAME_VERSION}");
Log.Debug("---- PATHS ----");
Log.Debug("Path to rundown: " + GameDataPath);
Log.Debug("Path to custom content: " + CustomPath);
Log.Debug("---- FLAGS ----");
Log.Debug($"Has GameData Path? {HasGameDataPath}");
Log.Debug($"Using plugin GameData path? {IsPluginGameDataPath}");
Log.Debug($"Has Custom Content? {HasCustomContent}");
Log.Debug($"Hot Reload Enabled? {IsHotReloadEnabled}");
Log.Debug($"Verbose Logging? {IsVerbose}");
Log.Debug($"Dump Game Data? {DumpGameData}");
Log.Debug($"Are Achievements Disabled? {DisableAchievements}");
Log.Debug("---- DEBUG END ----");
}
private static int GetGameVersion()
{
return CellBuildData.GetRevision();
}
}
public class ContentManager
{
private readonly global::MTFO.Utilities.JsonSerializer json = new global::MTFO.Utilities.JsonSerializer();
private readonly Dictionary<string, Action<string>> Handlers;
public ScanHolder ScanHolder;
public GlowstickHolder GlowstickHolder;
public TierNames TierNames;
public ContentManager()
{
Handlers = new Dictionary<string, Action<string>>
{
{ "puzzletypes.json", SetupChainedPuzzles },
{ "glowsticks.json", SetupGlowsticks },
{ "tiernames.json", SetupTierNames }
};
Init();
}
private void Init()
{
if (!ConfigManager.HasCustomContent)
{
return;
}
foreach (string key in Handlers.Keys)
{
if (PathUtil.CheckCustomFile(key, out var CombinedPath))
{
Handlers.TryGetValue(key, out var value);
try
{
value?.Invoke(CombinedPath);
}
catch (Exception msg)
{
Log.Error(msg);
}
Log.Debug(CombinedPath);
}
}
}
public void SetupChainedPuzzles(string path)
{
Log.Debug("Custom puzzles found");
ScanHolder = json.Deserialize<ScanHolder>(File.ReadAllText(path));
}
public void SetupGlowsticks(string path)
{
Log.Debug("Custom glowsticks found");
GlowstickHolder = json.Deserialize<GlowstickHolder>(File.ReadAllText(path));
GlowstickHolder.Setup();
}
public void SetupTierNames(string path)
{
TierNames = json.Deserialize<TierNames>(File.ReadAllText(path));
}
}
}
namespace MTFO.HotReload
{
internal class HotGameDataManager : IHotManager
{
public void OnHotReload(int id)
{
GameDataInit.ReInitialize();
Log.Verbose("Reinitialized GameData");
}
}
internal class HotGearManager : IHotManager
{
private readonly int gearSlotsTotal = 3;
public void OnHotReload(int id)
{
GearManager.Current.m_offlineSetupDone = false;
CleanGearIcons();
CleanGearSlots();
GearManager.Current.SetupGearContainers();
LoadOfflineGearDatas();
GearManager.GenerateAllGearIcons();
GearManager.Current.m_offlineSetupDone = true;
Log.Verbose("Reloaded Gear");
}
private void CleanGearIcons()
{
GearManager.m_allGearWithPostedIconJobs.Clear();
foreach (Dictionary<uint, RenderTexture> item in (Il2CppArrayBase<Dictionary<uint, RenderTexture>>)(object)GearManager.Current.m_allGearIconTexturesPerInstanceKey)
{
item.Clear();
}
}
[Obsolete]
private void CleanGearLobbySlots()
{
if (!((Object)(object)CM_PageLoadout.Current != (Object)null))
{
return;
}
foreach (CM_InventorySlotItem componentsInChild in ((Component)CM_PageLoadout.Current.m_popupAlign).gameObject.GetComponentsInChildren<CM_InventorySlotItem>(true))
{
Object.Destroy((Object)(object)((Component)componentsInChild).gameObject);
}
}
private void CleanGearSlots()
{
for (int i = 0; i < gearSlotsTotal; i++)
{
((Il2CppArrayBase<List<GearIDRange>>)(object)GearManager.Current.m_gearPerSlot)[i].Clear();
}
}
private void LoadOfflineGearDatas()
{
Il2CppArrayBase<PlayerOfflineGearDataBlock> allBlocks = GameDataBlockBase<PlayerOfflineGearDataBlock>.GetAllBlocks();
if (allBlocks == null)
{
Log.Warn("Unable to get Player Offline Gear blocks");
return;
}
Log.Verbose($"Loading {allBlocks.Length} gear");
foreach (PlayerOfflineGearDataBlock item in allBlocks)
{
OfflineGear.Load(item);
}
}
}
public class HotReloader : MonoBehaviour
{
public static HotReloader Current;
private CM_Item button;
private readonly string buttonLabel = "Reload Game Data";
private readonly Vector3 buttonPosition = new Vector3(0f, 77f, 0f);
private readonly List<IHotManager> managers = new List<IHotManager>();
public HotReloader(IntPtr intPtr)
: base(intPtr)
{
}//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
private void Awake()
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
((Component)this).gameObject.transform.localPosition = buttonPosition;
button = ((Component)this).gameObject.GetComponent<CM_Item>();
button.SetText(buttonLabel);
AddOnReloadListener(new HotGameDataManager());
AddOnReloadListener(new HotRundownManager());
AddOnReloadListener(new HotGearManager());
button.OnBtnPressCallback += Action<int>.op_Implicit((Action<int>)delegate
{
MTFOHotReloadAPI.HotReloaded();
});
}
public void AddOnReloadListener(IHotManager manager)
{
if (!managers.Contains(manager))
{
button.OnBtnPressCallback += Action<int>.op_Implicit((Action<int>)manager.OnHotReload);
managers.Add(manager);
}
}
public void RemoveOnReloadListener(IHotManager manager)
{
if (managers.Contains(manager))
{
button.OnBtnPressCallback -= Action<int>.op_Implicit((Action<int>)manager.OnHotReload);
managers.Remove(manager);
}
}
public static void Setup()
{
if (!((Object)(object)Current != (Object)null) && !((Object)(object)MainMenuGuiLayer.Current.PageRundownNew == (Object)null))
{
GameObject obj = Object.Instantiate<GameObject>(((Component)MainMenuGuiLayer.Current.PageRundownNew.m_discordButton).gameObject, ((Component)MainMenuGuiLayer.Current.PageRundownNew.m_discordButton).transform.parent, false);
((Object)obj).name = "Button HotReload";
Current = obj.AddComponent<HotReloader>();
obj.SetActive(true);
Log.Verbose("Created hot reload button");
}
}
}
public class HotReloadInjector
{
[HarmonyPostfix]
[HarmonyPatch(typeof(CM_PageRundown_New), "OnEnable")]
public static void OnEnable()
{
HotReloader.Setup();
}
}
internal class HotRundownManager : IHotManager
{
private CM_PageRundown_New rundownPage;
private bool hasValidRundown => GameDataBlockBase<RundownDataBlock>.s_blockByID.ContainsKey(Global.RundownIdToLoad);
private RundownDataBlock rundownDataCurrent => GameDataBlockBase<RundownDataBlock>.GetBlock(Global.RundownIdToLoad);
public HotRundownManager()
{
rundownPage = MainMenuGuiLayer.Current.PageRundownNew;
}
public void OnHotReload(int id)
{
if (hasValidRundown)
{
rundownPage.m_dataIsSetup = false;
CleanIconsOfTier();
TryPlaceRundown();
}
else
{
Log.Warn($"Failed to place the rundown due to missing Rundown id {Global.RundownIdToLoad}");
}
Log.Verbose("Reloaded Rundown");
}
private void CleanIconsOfTier()
{
CleanIconsOfTier(rundownPage.m_expIconsTier1);
CleanIconsOfTier(rundownPage.m_expIconsTier2);
CleanIconsOfTier(rundownPage.m_expIconsTier3);
CleanIconsOfTier(rundownPage.m_expIconsTier4);
CleanIconsOfTier(rundownPage.m_expIconsTier5);
}
private void CleanIconsOfTier(List<CM_ExpeditionIcon_New> tier)
{
Enumerator<CM_ExpeditionIcon_New> enumerator = tier.GetEnumerator();
while (enumerator.MoveNext())
{
Object.Destroy((Object)(object)((Component)enumerator.Current).gameObject);
}
}
private void TryPlaceRundown()
{
rundownPage.m_currentRundownData = rundownDataCurrent;
if (rundownPage.m_currentRundownData != null)
{
rundownPage.PlaceRundown(rundownPage.m_currentRundownData);
rundownPage.m_dataIsSetup = true;
}
else
{
Log.Warn("Unable to place rundown due to null data during reload");
}
}
}
public interface IHotManager
{
void OnHotReload(int id);
}
public class OfflineGear
{
private GearIDRange gearIDRange;
private ItemDataBlock itemData;
private int inventorySlot;
private uint persistentID;
private OfflineGear(PlayerOfflineGearDataBlock block)
{
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
if (TryParseGearJson(block.GearJSON, out gearIDRange))
{
gearIDRange.PlayfabItemId = ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name;
gearIDRange.PlayfabItemInstanceId = $"OfflineGear_ID_{((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).persistentID}";
gearIDRange.OfflineGearType = block.Type;
}
if (TryParseGearID(gearIDRange, out itemData, out inventorySlot))
{
persistentID = ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).persistentID;
}
else
{
Log.Warn($"Unable to construct Offline Gear [{((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).persistentID}] {((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name}");
}
}
public static void Load(PlayerOfflineGearDataBlock block)
{
if (TryParse(block, out var result) && TryStash(result))
{
Log.Verbose($"Loaded offline gear [{((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).persistentID}] {((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name}");
}
}
private static bool TryParse(PlayerOfflineGearDataBlock block, out OfflineGear result)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Invalid comparison between Unknown and I4
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
eOfflineGearType type = block.Type;
if (type - 1 <= 1)
{
result = new OfflineGear(block);
return true;
}
result = null;
Log.Warn($"Unimplemented Offline Gear Type [{((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).persistentID}] {block.Type}");
return false;
}
private bool TryParseGearID(GearIDRange gearIDRange, out ItemDataBlock itemData, out int inventorySlot)
{
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Expected I4, but got Unknown
inventorySlot = 0;
itemData = null;
if (gearIDRange == null)
{
Log.Warn("Unable to parse GearIDRange due to it being null");
return false;
}
uint compID = gearIDRange.GetCompID((eGearComponent)3);
itemData = ((compID != 0) ? GameDataBlockBase<ItemDataBlock>.GetBlock(compID) : null);
if (itemData == null)
{
Log.Warn($"Invalid ItemDataBlock for component in offlinear gear [c:{compID}]");
return false;
}
inventorySlot = (int)itemData.inventorySlot;
return true;
}
private bool TryParseGearJson(string gearJson, out GearIDRange gearIDRange)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Expected O, but got Unknown
if (string.IsNullOrEmpty(gearJson))
{
gearIDRange = null;
Log.Warn("Unable to assign GearIDRange due to null or empty GearJson");
return false;
}
gearIDRange = new GearIDRange(gearJson);
if (gearIDRange == null)
{
return false;
}
return true;
}
private static bool TryStash(OfflineGear gear)
{
if (gear == null)
{
Log.Warn("Unable to stash due to null offline gear");
return false;
}
if (gear.gearIDRange == null)
{
Log.Warn($"Unable to stash offline gear due to null GearIDRange [{gear.persistentID}]");
return false;
}
if (gear.itemData == null)
{
Log.Warn($"Unable to stash offline gear due to null ItemDataBlock [{gear.persistentID}]");
return false;
}
((Il2CppArrayBase<List<GearIDRange>>)(object)GearManager.Current.m_gearPerSlot)[gear.inventorySlot].Add(gear.gearIDRange);
return true;
}
}
}
namespace MTFO.CustomCP
{
public static class CustomPuzzleManager
{
private static readonly Dictionary<uint, CustomBioScan> s_scanMap = new Dictionary<uint, CustomBioScan>();
private static readonly Dictionary<uint, CustomClusterScan> s_clusterMap = new Dictionary<uint, CustomClusterScan>();
private static bool s_initialized = false;
public static bool TryGetScanByID(uint id, out CustomBioScan scan)
{
return s_scanMap.TryGetValue(id, out scan);
}
public static bool TryGetClusterByID(uint id, out CustomClusterScan cluster)
{
return s_clusterMap.TryGetValue(id, out cluster);
}
internal static void Initialize(ChainedPuzzleManager manager)
{
if (!s_initialized)
{
s_initialized = true;
ClassInjector.RegisterTypeInIl2Cpp<CorePuzzleData>();
ClassInjector.RegisterTypeInIl2Cpp<ClusterPuzzleData>();
if (ConfigManager.CustomContent.ScanHolder != null)
{
InitScans(manager);
InitClusters(manager);
}
}
}
private static void InitScans(ChainedPuzzleManager manager)
{
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_015a: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)manager == (Object)null)
{
Log.Error("Attempted to initialize custom bioscans with a null ChainedPuzzleManager. This should not happen!");
throw new NullReferenceException("ChainedPuzzleManager was null");
}
foreach (CustomBioScan scan in ConfigManager.CustomContent.ScanHolder.Scans)
{
Log.Debug($"Adding scan with ID of [{scan.PersistentID}]");
if (!manager.m_puzzleComponentPrefabs.ContainsKey(scan.BaseScan))
{
Log.Error($"Custom scan with persistent ID {scan.PersistentID} references a non-existing base scan of persistent ID {scan.BaseScan}");
continue;
}
GameObject val = manager.m_puzzleComponentPrefabs[scan.BaseScan];
if ((Object)(object)val.GetComponent<CP_Bioscan_Core>() == (Object)null)
{
Log.Error(string.Format("BaseScan id: {0} does not have {1} component!", scan.PersistentID, "CP_Bioscan_Core"));
continue;
}
GameObject val2 = Object.Instantiate<GameObject>(val);
val2.transform.position = new Vector3(10000f, 10000f, 10000f);
CP_PlayerScanner component = val2.GetComponent<CP_PlayerScanner>();
if ((Object)(object)component != (Object)null)
{
component.m_reduceSpeed = scan.ReduceSpeed;
component.m_reduceWhenNoPlayer = scan.ReduceWhenNoPlayer;
component.m_scanRadius = scan.ScanRadius;
component.m_scanSpeeds = Il2CppStructArray<float>.op_Implicit(scan.PlayersInScanMulti);
component.m_playerRequirement = scan.PlayerRequirement;
}
else
{
Log.Warn(string.Format("BaseScan id: {0} does not have {1} component! This will make following setting won't work!", scan.BaseScan, "CP_PlayerScanner"));
Log.Warn(" - ReduceSpeed");
Log.Warn(" - ReduceWhenNoPlayer");
Log.Warn(" - ScanRadius");
Log.Warn(" - PlayersInScanMulti");
Log.Warn(" - PlayerRequirement");
}
CP_Bioscan_Graphics component2 = val2.GetComponent<CP_Bioscan_Graphics>();
if ((Object)(object)component2 != (Object)null)
{
component2.m_radius = scan.BioScanGraphics.Radius;
component2.m_colors = Il2CppReferenceArray<ColorModeColor>.op_Implicit(ConvertToColorMode(scan.BioScanGraphics.ColorModeColor));
component2.SetText(scan.BioScanGraphics.ScanText);
}
else
{
Log.Warn(string.Format("BaseScan id: {0} does not have {1} component! This will make {2} setting won't work!", scan.BaseScan, "CP_Bioscan_Graphics", "BioScanGraphics"));
}
val2.GetComponent<CP_Bioscan_Core>().m_playerAgents = new List<PlayerAgent>();
val2.AddComponent<CorePuzzleData>().PersistentID.Set(scan.PersistentID);
s_scanMap.Add(scan.PersistentID, scan);
manager.m_puzzleComponentPrefabs.Add(scan.PersistentID, val2);
}
}
private static void InitClusters(ChainedPuzzleManager manager)
{
//IL_013d: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)manager == (Object)null)
{
Log.Error("Attempted to initialize custom clusters with a null ChainedPuzzleManager. This should not happen!");
throw new NullReferenceException("ChainedPuzzleManager was null");
}
foreach (CustomClusterScan cluster in ConfigManager.CustomContent.ScanHolder.Clusters)
{
Log.Debug($"Adding cluster with ID of [{cluster.PersistentID}]");
if (!manager.m_puzzleComponentPrefabs.ContainsKey(cluster.BaseCluster))
{
Log.Error($"Custom cluster scan with persistent ID {cluster.PersistentID} references a non-existing base cluster of persistent ID {cluster.BaseCluster}");
continue;
}
if (!manager.m_puzzleComponentPrefabs.ContainsKey(cluster.BioscanID))
{
Log.Error($"Custom cluster scan with persistent ID {cluster.PersistentID} references a non-existing bioscan of persistent ID {cluster.BioscanID}");
continue;
}
GameObject val = manager.m_puzzleComponentPrefabs[cluster.BaseCluster];
if ((Object)(object)val.GetComponent<CP_Cluster_Core>() == (Object)null)
{
Log.Error(string.Format("BaseScan id: {0} does not have {1} component!", cluster.PersistentID, "CP_Cluster_Core"));
continue;
}
GameObject val2 = Object.Instantiate<GameObject>(val);
val2.transform.position = new Vector3(1000f, 1000f, 1000f);
CP_Cluster_Core component = val2.GetComponent<CP_Cluster_Core>();
GameObject childPuzzlePrefab = manager.m_puzzleComponentPrefabs[cluster.BioscanID];
component.m_amountOfPuzzles = cluster.ClusterCount;
component.m_childPuzzlePrefab = childPuzzlePrefab;
component.m_distanceBetween = cluster.DistanceBetweenScans;
component.m_revealWithHoloPath = cluster.RevealWithHoloPath;
val2.AddComponent<ClusterPuzzleData>().PersistentID.Set(cluster.PersistentID);
s_clusterMap.Add(cluster.PersistentID, cluster);
manager.m_puzzleComponentPrefabs.Add(cluster.PersistentID, val2);
}
}
private static ColorModeColor[] ConvertToColorMode(CustomBioScan.BioScanColorByMode[] bioScanColorByModes)
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Expected O, but got Unknown
ColorModeColor[] array = (ColorModeColor[])(object)new ColorModeColor[bioScanColorByModes.Length];
for (int i = 0; i < bioScanColorByModes.Length; i++)
{
CustomBioScan.BioScanColorByMode bioScanColorByMode = bioScanColorByModes[i];
array[i] = new ColorModeColor
{
col = new Color(bioScanColorByMode.R, bioScanColorByMode.G, bioScanColorByMode.B, bioScanColorByMode.A),
mode = bioScanColorByMode.Mode
};
}
return array;
}
}
}
namespace MTFO.CustomCP.Patches
{
[HarmonyPatch(typeof(ChainedPuzzleManager), "OnAssetsLoaded")]
internal static class Patch_PuzzleManager
{
internal static void Postfix(ChainedPuzzleManager __instance)
{
CustomPuzzleManager.Initialize(__instance);
}
}
[HarmonyPatch(typeof(CP_Bioscan_Core), "Update")]
internal static class FixNullRefSpam
{
internal static bool Prefix(CP_Bioscan_Core __instance)
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
if (((Component)__instance).gameObject.transform.position.x > 9000f && ((Component)__instance).gameObject.transform.position.y > 9000f && ((Component)__instance).gameObject.transform.position.z > 9000f)
{
return false;
}
return true;
}
}
[HarmonyPatch]
internal static class Patch_SetupPuzzleCores
{
[HarmonyPostfix]
[HarmonyWrapSafe]
[HarmonyPatch(typeof(CP_Bioscan_Core), "Setup")]
public static void SetupSpline(CP_Bioscan_Core __instance)
{
CorePuzzleData component = ((Component)__instance).GetComponent<CorePuzzleData>();
if (!((Object)(object)component == (Object)null) && CustomPuzzleManager.TryGetScanByID(component.PersistentID.Value, out var scan))
{
CP_Holopath_Spline val = ((Il2CppObjectBase)__instance.m_spline).TryCast<CP_Holopath_Spline>();
if ((Object)(object)val != (Object)null)
{
scan.ApplySplineRevealSpeed(val);
}
}
}
[HarmonyPostfix]
[HarmonyWrapSafe]
[HarmonyPatch(typeof(CP_Cluster_Core), "Setup")]
public static void SetupSpline(CP_Cluster_Core __instance)
{
ClusterPuzzleData component = ((Component)__instance).GetComponent<ClusterPuzzleData>();
if (!((Object)(object)component == (Object)null) && CustomPuzzleManager.TryGetClusterByID(component.PersistentID.Value, out var cluster))
{
CP_Holopath_Spline val = ((Il2CppObjectBase)__instance.m_spline).TryCast<CP_Holopath_Spline>();
if ((Object)(object)val != (Object)null)
{
cluster.ApplySplineRevealSpeed(val);
}
}
}
}
}
namespace MTFO.Custom
{
public class GlowstickHolder
{
[JsonIgnore]
public Dictionary<string, CustomGlowstick> GlowstickLookup;
public List<GlowstickConfig> Glowsticks { get; set; }
public void Setup()
{
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
GlowstickLookup = new Dictionary<string, CustomGlowstick>();
foreach (GlowstickConfig glowstick in Glowsticks)
{
Log.Verbose(glowstick);
if (GlowstickLookup.TryGetValue(glowstick.Name, out var value))
{
Log.Warn("Custom glowstick with name " + glowstick.Name + " already exists in the lookup! Skipping...");
continue;
}
value = default(CustomGlowstick);
value.Color = new Color(glowstick.r, glowstick.g, glowstick.b, (glowstick.a == 0f) ? 1f : glowstick.a);
value.Range = ((glowstick.Range == 0f) ? 15f : glowstick.Range);
CustomGlowstick value2 = value;
GlowstickLookup.Add(glowstick.Name, value2);
}
}
}
public struct CustomGlowstick
{
public Color Color { get; set; }
public float Range { get; set; }
}
public struct GlowstickConfig
{
public string Name { get; set; }
public float Range { get; set; }
public float r { get; set; }
public float g { get; set; }
public float b { get; set; }
public float a { get; set; }
public override string ToString()
{
return $"{Name},{Range},{r},{g},{b},{a}";
}
}
public class ScanHolder
{
public List<CustomBioScan> Scans { get; set; }
public List<CustomClusterScan> Clusters { get; set; }
}
public enum RevealMode
{
ScaleByDistance,
ConstantTime,
Instant
}
public struct CustomBioScan : IRevealableScanConfig
{
public struct BioScanGx
{
public bool HideScanText { get; set; }
public string ScanText { get; set; }
public float Radius { get; set; }
public BioScanColorByMode[] ColorModeColor { get; set; }
}
public class BioScanColorByMode
{
public eChainedPuzzleGraphicsColorMode Mode { get; set; }
public float R { get; set; }
public float G { get; set; }
public float B { get; set; }
public float A { get; set; }
}
public uint BaseScan { get; set; }
public uint PersistentID { get; set; }
public PlayerRequirement PlayerRequirement { get; set; }
public float ScanRadius { get; set; }
public float[] PlayersInScanMulti { get; set; }
public float ReduceSpeed { get; set; }
public bool ReduceWhenNoPlayer { get; set; }
public float RevealTime { get; set; }
public RevealMode RevealMode { get; set; }
public BioScanGx BioScanGraphics { get; set; }
}
public struct CustomClusterScan : IRevealableScanConfig
{
public uint BaseCluster { get; set; }
public uint PersistentID { get; set; }
public int ClusterCount { get; set; }
public uint BioscanID { get; set; }
public float DistanceBetweenScans { get; set; }
public float RevealTime { get; set; }
public RevealMode RevealMode { get; set; }
public bool RevealWithHoloPath { get; set; }
}
internal interface IRevealableScanConfig
{
uint PersistentID { get; }
float RevealTime { get; }
RevealMode RevealMode { get; }
}
internal static class IRevealibleScanConfigExtensions
{
public static void ApplySplineRevealSpeed(this IRevealableScanConfig scan, CP_Holopath_Spline spline)
{
float revealTime = scan.RevealTime;
switch (scan.RevealMode)
{
case RevealMode.ConstantTime:
if (revealTime <= 0f)
{
Log.Warn($"Attempted to set a custom scan with persistent id '{scan.PersistentID}' to reveal in less than or equal to 0 seconds. This is not supported. Instead, use RevealMode \"{RevealMode.Instant}\" or integer value {2}");
break;
}
spline.m_splineLength = 1f;
spline.m_revealSpeed = revealTime;
break;
case RevealMode.ScaleByDistance:
if (revealTime < 0f)
{
Log.Warn($"Attempted to set a custom scan with persistent id '{scan.PersistentID}' to reveal in less than 0 seconds. This is not supported.");
}
else if (revealTime != 0f)
{
spline.m_revealSpeed = revealTime;
}
break;
case RevealMode.Instant:
spline.m_revealSpeed = -1f;
break;
}
}
}
public struct TierNames
{
public string Tier1 { get; set; }
public string Tier2 { get; set; }
public string Tier3 { get; set; }
public string Tier4 { get; set; }
public string Tier5 { get; set; }
}
}
namespace MTFO.Custom.CCP.Patches
{
[HarmonyPatch(typeof(CP_Bioscan_Hud))]
internal static class Patch_Bioscan_Hud
{
[HarmonyPatch("Awake")]
[HarmonyPostfix]
internal static void Post_Setup(CP_Bioscan_Hud __instance)
{
UpdateText(__instance);
}
[HarmonyPatch("SetVisible")]
[HarmonyPostfix]
internal static void Post_SetVisible(CP_Bioscan_Hud __instance)
{
UpdateText(__instance);
}
[HarmonyPatch("UpdateText")]
[HarmonyPostfix]
internal static void Post_UpdateText(CP_Bioscan_Hud __instance)
{
UpdateText(__instance);
}
private static void UpdateText(CP_Bioscan_Hud hud)
{
CorePuzzleData component = ((Component)hud).gameObject.GetComponent<CorePuzzleData>();
if ((Object)(object)component != (Object)null)
{
if (!CustomPuzzleManager.TryGetScanByID(component.PersistentID.Value, out var scan))
{
Log.Error($"scanData missing... {component.PersistentID.Value}");
}
else if (scan.BioScanGraphics.HideScanText)
{
((TMP_Text)hud.m_bioscanWorldText).SetText(string.Empty, true);
}
else if (!string.IsNullOrWhiteSpace(scan.BioScanGraphics.ScanText))
{
((TMP_Text)hud.m_bioscanWorldText).SetText(scan.BioScanGraphics.ScanText, true);
}
}
}
}
}
namespace MTFO.Custom.CCP.Components
{
internal sealed class ClusterPuzzleData : MonoBehaviour
{
public Il2CppValueField<uint> PersistentID;
public CP_Bioscan_Hud[] ChildHuds = Array.Empty<CP_Bioscan_Hud>();
}
internal sealed class CorePuzzleData : MonoBehaviour
{
public Il2CppValueField<uint> PersistentID;
}
}
namespace MTFO.API
{
public delegate void GameDataContentLoadEvent(string datablockName, string jsonContent, in List<string> jsonItemsToInject);
public delegate void GameDataContentLoadedDelegate(string datablockName, string jsonContent);
public static class MTFOGameDataAPI
{
public static event GameDataContentLoadEvent OnGameDataContentLoad;
public static event GameDataContentLoadedDelegate OnGameDataContentLoaded;
internal static void Invoke_OnGameDataContentLoad(string datablockName, Stream jsonContentStream, in List<string> jsonItemsToInject)
{
if (MTFOGameDataAPI.OnGameDataContentLoad != null)
{
MTFOGameDataAPI.OnGameDataContentLoad(datablockName, new StreamReader(jsonContentStream).ReadToEnd(), in jsonItemsToInject);
}
}
internal static void Invoke_OnGameDataContentLoaded(string datablockName, string jsonContent)
{
MTFOGameDataAPI.OnGameDataContentLoaded?.Invoke(datablockName, jsonContent);
}
}
public static class MTFOHotReloadAPI
{
public static bool HotReloadEnabled => ConfigManager.IsHotReloadEnabled;
public static event Action OnHotReload;
internal static void HotReloaded()
{
MTFOHotReloadAPI.OnHotReload?.Invoke();
}
}
public static class MTFOPathAPI
{
public static string RundownPath => ConfigManager.GameDataPath;
public static bool HasRundownPath => ConfigManager.HasGameDataPath;
public static string CustomPath => ConfigManager.CustomPath;
public static bool HasCustomPath => ConfigManager.HasCustomContent;
}
}