Decompiled source of WaveSurvival v1.1.1
WaveSurvival.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using AIGraph; using AK; using Agents; using AmorLib.Utils; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using Enemies; using GTFO.API; using GTFO.API.Utilities; using GameData; using HarmonyLib; using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using LevelGeneration; using Localization; using MTFO.API; using MTFO.Ext.PartialData; using MTFO.Ext.PartialData.JsonConverters; using Microsoft.CodeAnalysis; using Player; using SNetwork; using TMPro; using UnityEngine; using WaveSurvival.Attributes; using WaveSurvival.CustomWave; using WaveSurvival.CustomWaveData; using WaveSurvival.CustomWaveData.Wave; using WaveSurvival.CustomWaveData.WaveObjective; using WaveSurvival.Dependencies; using WaveSurvival.Extensions; using WaveSurvival.Json; using WaveSurvival.Json.Converters; using WaveSurvival.Json.Converters.Utils; using WaveSurvival.Networking; using WaveSurvival.Utils; using WaveSurvival.Utils.Extensions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("WaveSurvival")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+eb55afba14f084115c1fe6c1b19a57c0ec6eaed8")] [assembly: AssemblyProduct("WaveSurvival")] [assembly: AssemblyTitle("WaveSurvival")] [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 WaveSurvival { internal static class Configuration { private static ConfigEntry<KeyCode> _skipWaveBind; private static ConfigFile _configFile; public static KeyCode SkipWaveBind => _skipWaveBind.Value; internal static void Init() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown _configFile = new ConfigFile(Path.Combine(Paths.ConfigPath, "WaveSurvival.cfg"), true); string text = "Keybinds"; _skipWaveBind = _configFile.Bind<KeyCode>(text, "Skip Wave Keybind", (KeyCode)120, "The keybind to skip the wait until the next wave (host only)."); LiveEdit.CreateListener(Paths.ConfigPath, "WaveSurvival.cfg", false).FileChanged += new LiveEditEventHandler(OnFileChanged); } private static void OnFileChanged(LiveEditEventArgs _) { _configFile.Reload(); } } internal static class DinoLogger { private static ManualLogSource _logger = Logger.CreateLogSource("WaveSurvival"); public static void Log(string format, params object[] args) { Log(string.Format(format, args)); } public static void Log(string str) { if (_logger != null) { _logger.Log((LogLevel)8, (object)str); } } public static void Warning(string format, params object[] args) { Warning(string.Format(format, args)); } public static void Warning(string str) { if (_logger != null) { _logger.Log((LogLevel)4, (object)str); } } public static void Error(string format, params object[] args) { Error(string.Format(format, args)); } public static void Error(string str) { if (_logger != null) { _logger.Log((LogLevel)2, (object)str); } } public static void Debug(string format, params object[] args) { Debug(string.Format(format, args)); } public static void Debug(string str) { if (_logger != null) { _logger.Log((LogLevel)32, (object)str); } } } [BepInPlugin("Dinorush.WaveSurvival", "WaveSurvival", "1.1.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class EntryPoint : BasePlugin { public const string MODNAME = "WaveSurvival"; public const string AUTHOR = "Dinorush"; public const string GUID = "Dinorush.WaveSurvival"; public const string VERSION = "1.1.1"; private IEnumerable<MethodInfo> _cleanupCallbacks; private IEnumerable<MethodInfo> _enterCallbacks; private IEnumerable<MethodInfo> _buildStartCallbacks; private IEnumerable<MethodInfo> _buildDoneCallbacks; public override void Load() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) CacheFrequentCallbacks(); LevelAPI.OnLevelCleanup += RunFrequentCallback(_cleanupCallbacks); LevelAPI.OnEnterLevel += RunFrequentCallback(_enterCallbacks); LevelAPI.OnBuildStart += RunFrequentCallback(_buildStartCallbacks); LevelAPI.OnBuildDone += RunFrequentCallback(_buildDoneCallbacks); new Harmony("WaveSurvival").PatchAll(); AssetAPI.OnStartupAssetsLoaded += InvokeCallbacks<InvokeOnAssetsLoadedAttribute>; Configuration.Init(); InvokeCallbacks<InvokeOnLoadAttribute>(); DinoLogger.Log("Loaded WaveSurvival"); } private static Action RunFrequentCallback(IEnumerable<MethodInfo> callbacks) { IEnumerable<MethodInfo> callbacks2 = callbacks; return delegate { foreach (MethodInfo item in callbacks2) { item.Invoke(null, null); } }; } private void CacheFrequentCallbacks() { IEnumerable<MethodInfo> source = from method in ((IEnumerable<Type>)AccessTools.GetTypesFromAssembly(((object)this).GetType().Assembly)).SelectMany((Func<Type, IEnumerable<MethodInfo>>)AccessTools.GetDeclaredMethods) where method.IsStatic select method; var source2 = from method in source let attr = method.GetCustomAttribute<InvokeOnCleanupAttribute>() where attr != null select new { Method = method, Attribute = attr }; _cleanupCallbacks = source2.Select(pair => pair.Method); _enterCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute<InvokeOnEnterAttribute>() != null); _buildStartCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute<InvokeOnBuildStartAttribute>() != null); _buildDoneCallbacks = source.Where((MethodInfo method) => method.GetCustomAttribute<InvokeOnBuildDoneAttribute>() != null); } private void InvokeCallbacks<T>() where T : Attribute { foreach (MethodInfo item in from method in ((IEnumerable<Type>)AccessTools.GetTypesFromAssembly(((object)this).GetType().Assembly)).SelectMany((Func<Type, IEnumerable<MethodInfo>>)AccessTools.GetDeclaredMethods) where method.IsStatic && method.GetCustomAttribute<T>() != null select method) { item.Invoke(null, null); } } } } namespace WaveSurvival.Extensions { public static class CellSoundPlayerExt { public static uint PostWithCleanup(this CellSoundPlayer soundPlayer, uint eventID, Vector3 pos) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return soundPlayer.Post(eventID, pos, 1u, EventCallback.op_Implicit((Action<Object, AkCallbackType, AkCallbackInfo>)SoundDoneCallback), (Object)(object)soundPlayer); } public static void SoundDoneCallback(Object in_cookie, AkCallbackType in_type, AkCallbackInfo callbackInfo) { CellSoundPlayer obj = ((Il2CppObjectBase)in_cookie).Cast<CellSoundPlayer>(); if (obj != null) { obj.Recycle(); } } } } namespace WaveSurvival.Utils { internal class EmptyList<T> { public static readonly List<T> Instance = new List<T>(); } [JsonConverter(typeof(LocaleTextConverter))] public struct LocaleText : IEquatable<LocaleText> { public uint ID; public string RawText; public static readonly LocaleText Empty = new LocaleText(string.Empty); private readonly string TextFallback { get { if (ID != 0) { return Text.Get(ID); } return RawText; } } public LocaleText(LocalizedText baseText) { ID = 0u; RawText = string.Empty; RawText = LocalizedToText(baseText); ID = baseText.Id; } public LocaleText(string text) { ID = 0u; RawText = string.Empty; if (PartialData.TryGetGUID(text, out var guid)) { RawText = string.Empty; ID = guid; } else { RawText = text; ID = 0u; } } public LocaleText(uint id) { ID = 0u; RawText = string.Empty; RawText = string.Empty; ID = id; } public readonly LocalizedText ToLocalizedText() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown return new LocalizedText { Id = ID, UntranslatedText = TextFallback }; } public override readonly string ToString() { return TextFallback; } public static string LocalizedToText(LocalizedText text) { if (!text.HasTranslation) { return text.UntranslatedText; } return Text.Get(text.Id); } public static explicit operator LocaleText(LocalizedText localizedText) { return new LocaleText(localizedText); } public static explicit operator LocaleText(string text) { return new LocaleText(text); } public static implicit operator LocalizedText(LocaleText localeText) { return localeText.ToLocalizedText(); } public static implicit operator string(LocaleText localeText) { return localeText.ToString(); } public readonly bool Equals(LocaleText other) { if (ID == other.ID) { return string.Equals(RawText, other.RawText, StringComparison.Ordinal); } return false; } public override readonly bool Equals(object? obj) { if (obj is LocaleText other) { return Equals(other); } return false; } public override readonly int GetHashCode() { return HashCode.Combine(ID, RawText); } public static bool operator ==(LocaleText left, LocaleText right) { return left.Equals(right); } public static bool operator !=(LocaleText left, LocaleText right) { return !(left == right); } } [JsonConverter(typeof(WeightedListConverterFactory))] public sealed class WeightedList<T> : IEnumerable<T>, IEnumerable where T : IWeightable { private class Node { public T Value { get; set; } public float Weight { get; set; } public float TotalWeight { get; set; } public Node(T value) { Value = value; Weight = value.Weight; TotalWeight = Weight; } } public static readonly WeightedList<T> Empty = new WeightedList<T>(); private static readonly Random s_random = new Random(); private List<Node> _heap = EmptyList<Node>.Instance; private List<T> _values = EmptyList<T>.Instance; private bool _isDirty; public List<T> Values { get { return _values; } set { _values = value; _isDirty = true; } } private List<Node> Heap { get { if (_isDirty) { _isDirty = false; GenerateHeap(); } return _heap; } } public T this[int index] { get { return _values[index]; } set { _values[index] = value; _isDirty = true; } } public int Count => Values.Count; public WeightedList() { } public WeightedList(List<T> list) { Values = list; } public static implicit operator WeightedList<T>?(List<T>? list) { if (list == null) { return null; } return new WeightedList<T>(list); } public static implicit operator List<T>?(WeightedList<T>? weightedList) { return weightedList?.Values; } public void Add(T value) { if (Count == 0) { _values = new List<T>(); } _values.Add(value); _isDirty = true; } public void Remove(T value) { if (Count != 0) { _isDirty = _values.Remove(value); } } public void RemoveAt(int index) { if (Count != 0) { _isDirty = true; _values.RemoveAt(index); } } public int RemoveAll(Predicate<T> predicate) { int num = _values.RemoveAll(predicate); if (num != 0) { _isDirty = true; } return num; } IEnumerator IEnumerable.GetEnumerator() { return _values.GetEnumerator(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return _values.GetEnumerator(); } public bool TryGetRandom([MaybeNullWhen(false)] out T obj) { if (Count == 0 || Heap[1].TotalWeight == 0f) { obj = default(T); return false; } obj = PopFromHeap(); return true; } public T GetRandom() { if (Count == 0) { throw new IndexOutOfRangeException("Can't get random element from 0-length list!"); } if (Heap[1].TotalWeight == 0f) { RefillHeap(); } return PopFromHeap(); } public void Refill() { if (Count != 0 && !_isDirty) { RefillHeap(); } } private void GenerateHeap() { _heap = new List<Node>(Count + 1) { null }; foreach (T value in _values) { _heap.Add(new Node(value)); } for (int num = Heap.Count - 1; num > 1; num--) { _heap[num >> 1].TotalWeight += _heap[num].TotalWeight; } } private void RefillHeap() { for (int i = 1; i < Heap.Count; i++) { Heap[i].Weight = _values[i - 1].Weight; Heap[i].TotalWeight = _values[i - 1].Weight; } for (int num = Heap.Count - 1; num > 1; num--) { Heap[num >> 1].TotalWeight += Heap[num].TotalWeight; } } private T PopFromHeap() { if (Heap[1].TotalWeight == 0f) { throw new IndexOutOfRangeException("Cannot get an element from WeightedShuffleList with 0 total weight remaining!"); } if (Count == 1) { return _values[0]; } float num = s_random.NextSingle(Heap[1].TotalWeight); int num2 = 1; while (num >= Heap[num2].Weight) { num -= Heap[num2].Weight; num2 <<= 1; if (num >= Heap[num2].TotalWeight) { num -= Heap[num2].TotalWeight; num2++; } } float weight = Heap[num2].Weight; T value = Heap[num2].Value; Heap[num2].Weight = 0f; while (num2 > 0) { Heap[num2].TotalWeight -= weight; num2 >>= 1; } return value; } } public interface IWeightable { float Weight { get; } } } namespace WaveSurvival.Utils.Extensions { internal static class EnemyAgentExt { public static void AddOnDeadOnce(this EnemyAgent agent, Action onDead) { Action onDead2 = onDead; bool called = false; agent.OnDeadCallback += Action.op_Implicit((Action)delegate { if (!called && !CheckpointManager.IsReloadingCheckpoint) { onDead2?.Invoke(); called = true; } }); } } internal static class RandomExt { public static void Shuffle<T>(this Random random, T[] values) { int num = values.Length; for (int i = 0; i < num - 1; i++) { int num2 = random.Next(i, num); if (num2 != i) { int num3 = num2; int num4 = i; T val = values[i]; T val2 = values[num2]; values[num3] = val; values[num4] = val2; } } } public static float NextSingle(this Random random, float maxValue) { return random.NextSingle() * maxValue; } public static float NextSingle(this Random random, float minValue, float maxValue) { return random.NextSingle() * (maxValue - minValue) + minValue; } } internal static class StringExt { public static T ToEnum<T>(this string? value, T defaultValue) where T : struct { if (string.IsNullOrEmpty(value)) { return defaultValue; } if (!Enum.TryParse<T>(value.Replace(" ", null), ignoreCase: true, out var result)) { return defaultValue; } return result; } } } namespace WaveSurvival.Patches { [HarmonyPatch] internal static class CheckpointPatches { private static Vector3 _lastCheckpointPos = Vector3.zero; [HarmonyPatch(typeof(CheckpointManager), "OnStateChange")] [HarmonyWrapSafe] [HarmonyPostfix] public static void OnCheckpointStateChange(pCheckpointState newState) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if ((int)newState.lastInteraction == 1 && _lastCheckpointPos != newState.doorLockPosition) { _lastCheckpointPos = newState.doorLockPosition; WaveManager.Internal_OnCheckpointReached(); } else if ((int)newState.lastInteraction == 2) { WaveManager.Internal_OnCheckpointReload(); } } [InvokeOnCleanup] private static void OnCleanup() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) _lastCheckpointPos = Vector3.zero; } } [HarmonyPatch] internal static class GuiPatches { [HarmonyPatch(typeof(GuiManager), "Setup")] [HarmonyPostfix] private static void AddNewLayer() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) PUI_ObjectiveTimer waveInfo = ((Il2CppObjectBase)((GuiLayer)GuiManager.PlayerLayer).AddComp("Gui/Player/PUI_ObjectiveTimer", (GuiAnchor)3, new Vector2(0f, 425f), (Transform)null)).Cast<PUI_ObjectiveTimer>(); PUI_InteractionPrompt intPrompt = ((Il2CppObjectBase)((GuiLayer)GuiManager.InteractionLayer).AddRectComp("Gui/Player/PUI_InteractionPrompt_CellUI", (GuiAnchor)3, new Vector2(0f, 375f), (Transform)null)).Cast<PUI_InteractionPrompt>(); WaveText.Internal_Setup(waveInfo, intPrompt); } } [HarmonyPatch] internal static class LobbyPatches { [HarmonyPatch(typeof(SNet_SyncManager), "OnPlayerJoinedSessionHub")] [HarmonyPostfix] private static void Post_Joined(SNet_Player player) { if (WaveManager.TryGetActiveObjective(out WaveObjectiveData objective)) { WaveNetwork.SendObjective(objective, player); } } } [HarmonyPatch] internal static class WarpPatches { [HarmonyPatch(typeof(PlayerAgent), "WarpTo")] [HarmonyWrapSafe] [HarmonyPostfix] private static void PreWarp(PlayerAgent __instance, ref eDimensionIndex __state) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected I4, but got Unknown __state = (eDimensionIndex)(int)((Agent)__instance).DimensionIndex; } [HarmonyPatch(typeof(PlayerAgent), "WarpTo")] [HarmonyWrapSafe] [HarmonyPostfix] private static void OnWarp(PlayerAgent __instance, eDimensionIndex __state) { //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_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) eDimensionIndex dimensionIndex = ((Agent)__instance).DimensionIndex; if (__state != dimensionIndex) { WaveManager.Internal_OnWarp(dimensionIndex); } } } [HarmonyPatch] internal class Patch_CheckAndExecuteEventsOnTrigger { [HarmonyPrefix] [HarmonyWrapSafe] [HarmonyPatch(typeof(WardenObjectiveManager), "CheckAndExecuteEventsOnTrigger", new Type[] { typeof(WardenObjectiveEventData), typeof(eWardenObjectiveEventTrigger), typeof(bool), typeof(float) })] private static void Pre_CheckAndExecuteEventsOnTrigger(WardenObjectiveEventData eventToTrigger, eWardenObjectiveEventTrigger trigger, bool ignoreTrigger, float currentDuration) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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) if (eventToTrigger != null && (ignoreTrigger || eventToTrigger.Trigger == trigger) && ((double)currentDuration == 0.0 || !(eventToTrigger.Delay <= currentDuration))) { WaveManager.Internal_OnWardenEvent(eventToTrigger.Type); } } } } namespace WaveSurvival.Networking { public abstract class SyncedEvent<T> where T : struct { public delegate void ReceiveHandler(T packet); private bool _isSetup; public abstract string GUID { get; } public bool IsSetup => _isSetup; public string EventName { get; private set; } = string.Empty; public event ReceiveHandler? OnReceive; public event ReceiveHandler? OnReceiveLocal; public void Setup() { if (!_isSetup) { EventName = "SIM" + GUID; NetworkAPI.RegisterEvent<T>(EventName, (Action<ulong, T>)ReceiveClient_Callback); _isSetup = true; } } public void Send(T packetData, SNet_Player? target = null, SNet_ChannelType priority = 4) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target != (Object)null) { NetworkAPI.InvokeEvent<T>(EventName, packetData, target, priority); } else { NetworkAPI.InvokeEvent<T>(EventName, packetData, priority); } ReceiveLocal_Callback(packetData); } private void ReceiveLocal_Callback(T packet) { ReceiveLocal(packet); this.OnReceiveLocal?.Invoke(packet); } private void ReceiveClient_Callback(ulong sender, T packet) { Receive(packet); this.OnReceive?.Invoke(packet); } protected virtual void ReceiveLocal(T packet) { } protected virtual void Receive(T packet) { } } public abstract class SyncedEventMasterOnly<T> : SyncedEvent<T> where T : struct { public void Send(T packet, SNet_ChannelType priority = 4) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!SNet.IsMaster) { Send(packet, SNet.Master, priority); } else { Receive(packet); } } } } namespace WaveSurvival.Json { public static class JSON { private static readonly JsonSerializerOptions _setting; static JSON() { _setting = new JsonSerializerOptions { ReadCommentHandling = JsonCommentHandling.Skip, IncludeFields = true, PropertyNameCaseInsensitive = true, WriteIndented = true, IgnoreReadOnlyProperties = true }; _setting.Converters.Add(new JsonStringEnumConverter()); _setting.Converters.Add(new OptionalListConverter<WaveObjectiveData>()); if (PartialData.HasPData) { _setting.Converters.Add(PartialData.PDataIDConverter); } } public static T? Deserialize<T>(string json) { return JsonSerializer.Deserialize<T>(json, _setting); } public static T? Deserialize<T>(ref Utf8JsonReader reader) { return JsonSerializer.Deserialize<T>(ref reader, _setting); } public static bool TryDeserializeSafe<T>(string json, [MaybeNullWhen(false)] out T value) { try { value = JsonSerializer.Deserialize<T>(json, _setting); return value != null; } catch (JsonException ex) { DinoLogger.Error("Caught exception while reading json: " + ex.Message + "\n" + ex.StackTrace); value = default(T); return false; } } public static bool TryDeserialize<T>(string json, [MaybeNullWhen(false)] out T value) { value = JsonSerializer.Deserialize<T>(json, _setting); return value != null; } public static bool TryDeserialize<T>(ref Utf8JsonReader reader, [MaybeNullWhen(false)] out T value) { value = JsonSerializer.Deserialize<T>(ref reader, _setting); return value != null; } public static object? Deserialize(Type type, string json) { return JsonSerializer.Deserialize(json, type, _setting); } public static string Serialize<T>(T value) { return JsonSerializer.Serialize(value, _setting); } public static void Serialize<T>(Utf8JsonWriter writer, T value) { JsonSerializer.Serialize(writer, value, _setting); } public static void Serialize<T>(Utf8JsonWriter writer, string name, T value) { writer.WritePropertyName(name); JsonSerializer.Serialize(writer, value, _setting); } } [JsonConverter(typeof(JsonReferenceConverterFactory))] public class JsonReference<T> where T : new() { public virtual T? Value { get; set; } public string ID { get; set; } = string.Empty; public JsonReference() { } public JsonReference(T? obj) { Value = obj; } public JsonReference(string id) { ID = id; } public static implicit operator T(JsonReference<T> r) { if (r.Value != null) { return r.Value; } throw new InvalidOperationException(typeof(T).Name + " reference has not been resolved (" + r.ID + ")"); } } } namespace WaveSurvival.Json.Converters { public sealed class JsonReferenceConverterFactory : JsonConverterFactory { public override bool CanConvert(Type typeToConvert) { if (!typeToConvert.IsGenericType) { return false; } return typeToConvert.GetGenericTypeDefinition().IsAssignableTo(typeof(JsonReference<>)); } public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) { Type type = typeToConvert.GetGenericArguments()[0]; return (JsonConverter)Activator.CreateInstance(typeof(JsonReferenceConverter<>).MakeGenericType(type)); } } public sealed class JsonReferenceConverter<T> : JsonConverter<JsonReference<T>> where T : new() { public override bool HandleNull => true; public override JsonReference<T>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return reader.TokenType switch { JsonTokenType.Null => new JsonReference<T>(), JsonTokenType.String => new JsonReference<T>(JsonSerializer.Deserialize<string>(ref reader, options)), _ => new JsonReference<T>(JsonSerializer.Deserialize<T>(ref reader, options)), }; } public override void Write(Utf8JsonWriter writer, JsonReference<T>? value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); } else if (value.Value != null) { JsonSerializer.Serialize(writer, value.Value, options); } else if (!string.IsNullOrEmpty(value.ID)) { JsonSerializer.Serialize(writer, value.ID, options); } else { JsonSerializer.Serialize(writer, new T(), options); } } } public sealed class LevelTargetConverter : JsonConverter<LevelTarget> { public override bool HandleNull => true; public override LevelTarget? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { LevelTarget levelTarget = new LevelTarget(); if (ParseTarget(ref reader, levelTarget, options)) { return levelTarget; } throw new JsonException("Expected level target to be a tier, tierindex, or level layout ID"); } private static bool ParseTarget(ref Utf8JsonReader reader, LevelTarget target, JsonSerializerOptions options) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Invalid comparison between Unknown and I4 //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Invalid comparison between Unknown and I4 if (reader.TokenType == JsonTokenType.Null) { return true; } if (reader.TokenType == JsonTokenType.String) { string @string = reader.GetString(); if (@string.Length >= 5) { target.Tier = @string.Substring(0, 5).ToEnum<eRundownTier>((eRundownTier)99); } else { target.Tier = ("Tier" + @string[0]).ToEnum<eRundownTier>((eRundownTier)99); } int num = ((@string.Length < 5) ? 1 : 5); if ((int)target.Tier != 99 && @string.Length > num) { string text = @string; int num2 = num; if (int.TryParse(text.Substring(num2, text.Length - num2), out var result)) { target.TierIndex = result - 1; } else { target.Tier = (eRundownTier)99; } } if ((int)target.Tier == 99) { target.LevelLayoutID = JsonSerializer.Deserialize<uint>(ref reader, options); } return true; } if (reader.TokenType == JsonTokenType.Number) { target.LevelLayoutID = reader.GetUInt32(); return true; } return false; } public override void Write(Utf8JsonWriter writer, LevelTarget? value, JsonSerializerOptions options) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (value == null || (value.LevelLayoutID == 0 && (int)value.Tier == 99)) { writer.WriteNullValue(); return; } if (value.LevelLayoutID != 0) { writer.WriteNumberValue(value.LevelLayoutID); return; } eRundownTier tier = value.Tier; string? text = ((object)(eRundownTier)(ref tier)).ToString(); writer.WriteStringValue(text[text.Length - 1] + ((value.TierIndex >= 0) ? (value.TierIndex + 1).ToString() : "")); } } public sealed class SpawnDataConverter : JsonConverter<SpawnData> { public override SpawnData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { SpawnData spawnData = new SpawnData(); if (reader.TokenType == JsonTokenType.Number) { WeightedEnemyData item = new WeightedEnemyData { ID = reader.GetUInt32() }; spawnData.Enemies = new JsonReference<List<WeightedEnemyData>>(new List<WeightedEnemyData> { item }); return spawnData; } if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException("Expected number/list/object when reading a SpawnData object"); } while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) { return spawnData; } if (reader.TokenType != JsonTokenType.PropertyName) { throw new JsonException("Expected PropertyName when reading SpawnData object"); } string text = reader.GetString().ToLower(); reader.Read(); switch (text) { case "enemies": { if (JSON.TryDeserialize<JsonReference<List<WeightedEnemyData>>>(ref reader, out var value2)) { spawnData.Enemies = value2; } break; } case "count": spawnData.Count = reader.GetInt32(); break; case "spawnrate": spawnData.SpawnRate = reader.GetSingle(); break; case "spawninterval": spawnData.SpawnInterval = reader.GetInt32(); break; case "spawndelayoninterval": spawnData.SpawnDelayOnInterval = reader.GetSingle(); break; case "randomdirectionchanceoninterval": spawnData.RandomDirectionChanceOnInterval = reader.GetSingle(); break; case "subwavemaxcount": spawnData.SubWaveMaxCount = reader.GetInt32(); break; case "subwavedelay": spawnData.SubWaveDelay = reader.GetSingle(); break; case "randomdirectionchance": spawnData.RandomDirectionChance = reader.GetSingle(); break; case "eventsonsubwavestart": { if (JSON.TryDeserialize<List<WardenObjectiveEventData>>(ref reader, out var value)) { spawnData.EventsOnSubWaveStart = value; } break; } case "subwavescreamsize": spawnData.SubWaveScreamSize = JsonSerializer.Deserialize<ScreamSize>(ref reader, options); break; case "subwavescreamtype": spawnData.SubWaveScreamType = JsonSerializer.Deserialize<ScreamType>(ref reader, options); break; case "spawnlocations": spawnData.SpawnLocations = JsonSerializer.Deserialize<List<SpawnPathData>>(ref reader, options); break; case "spawndistance": if (reader.TokenType == JsonTokenType.Null) { spawnData.SpawnDistance = null; } else { spawnData.SpawnDistance = reader.GetInt32(); } break; case "hidefromtotalcount": spawnData.HideFromTotalCount = reader.GetBoolean(); break; } } throw new JsonException("Expected EndObject when reading SpawnData object"); } public override void Write(Utf8JsonWriter writer, SpawnData? value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); return; } writer.WriteStartObject(); JSON.Serialize(writer, "Enemies", value.Enemies); writer.WriteNumber("Count", value.Count); writer.WriteNumber("SpawnRate", value.SpawnRate); writer.WriteNumber("SpawnInterval", value.SpawnInterval); writer.WriteNumber("SpawnDelayOnInterval", value.SpawnDelayOnInterval); writer.WriteNumber("RandomDirectionChanceOnInterval", value.RandomDirectionChanceOnInterval); writer.WriteNumber("SubWaveMaxCount", value.SubWaveMaxCount); writer.WriteNumber("SubWaveDelay", value.SubWaveDelay); writer.WriteNumber("RandomDirectionChance", value.RandomDirectionChance); JSON.Serialize(writer, "EventsOnSubWaveStart", value.EventsOnSubWaveStart); writer.WriteString("SubWaveScreamSize", value.SubWaveScreamSize.ToString()); writer.WriteString("SubWaveScreamType", value.SubWaveScreamType.ToString()); if (value.SpawnLocations != null) { JSON.Serialize(writer, "SpawnLocations", value.SpawnLocations); } else { writer.WriteNull("SpawnLocations"); } if (value.SpawnDistance.HasValue) { writer.WriteNumber("SpawnDistance", value.SpawnDistance.Value); } else { writer.WriteNull("SpawnDistance"); } writer.WriteBoolean("HideFromTotalCount", value.HideFromTotalCount); writer.WriteEndObject(); } } public sealed class SpawnPathDataConverter : JsonConverter<SpawnPathData> { public override SpawnPathData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { SpawnPathData spawnPathData = new SpawnPathData(); if (reader.TokenType == JsonTokenType.Number) { spawnPathData.ZoneIndex = (eLocalZoneIndex)reader.GetInt32(); return spawnPathData; } if (reader.TokenType != JsonTokenType.StartArray) { throw new JsonException("Expected number/list when reading a SpawnPath object"); } reader.Read(); if (reader.TokenType != JsonTokenType.Number) { throw new JsonException("Expected local index when reading a SpawnPath object"); } spawnPathData.ZoneIndex = (eLocalZoneIndex)reader.GetInt32(); reader.Read(); if (reader.TokenType == JsonTokenType.EndArray) { return spawnPathData; } if (reader.TokenType != JsonTokenType.Number && reader.TokenType != JsonTokenType.Null) { throw new JsonException("Expected area index when reading a SpawnPath object"); } spawnPathData.AreaIndex = ((reader.TokenType != JsonTokenType.Null) ? reader.GetInt32() : (-999)); reader.Read(); if (reader.TokenType == JsonTokenType.EndArray) { return spawnPathData; } if (reader.TokenType != JsonTokenType.Number) { throw new JsonException("Expected layer when reading a SpawnPath object"); } spawnPathData.Layer = (LG_LayerType)(byte)reader.GetInt32(); reader.Read(); if (reader.TokenType != JsonTokenType.EndArray) { throw new JsonException("Expected EndArray when reading SpawnPath object"); } return spawnPathData; } public override void Write(Utf8JsonWriter writer, SpawnPathData? value, JsonSerializerOptions options) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected I4, but got Unknown //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected I4, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected I4, but got Unknown if (value == null) { writer.WriteNullValue(); return; } if (value.AreaIndex == -1) { writer.WriteNumberValue((int)value.ZoneIndex); return; } writer.WriteStartArray(); writer.WriteNumberValue((int)value.ZoneIndex); if (value.AreaIndex == -999) { writer.WriteNullValue(); } else { writer.WriteNumberValue(value.AreaIndex); } if ((int)value.Layer != 0) { writer.WriteNumberValue((int)value.Layer); } writer.WriteEndArray(); } } public sealed class WaveDataConverter : JsonConverter<WaveData> { public override WaveData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { WaveData waveData = new WaveData(); if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException("Expected object when reading a WaveData object"); } while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) { return waveData; } if (reader.TokenType != JsonTokenType.PropertyName) { throw new JsonException("Expected PropertyName when reading WaveData object"); } string text = reader.GetString().ToLower(); reader.Read(); switch (text) { case "spawns": { List<JsonReference<SpawnData>> value2; if (reader.TokenType == JsonTokenType.Number) { if (JSON.TryDeserialize<JsonReference<SpawnData>>(ref reader, out var value)) { waveData.Spawns = new List<JsonReference<SpawnData>> { value }; } } else if (JSON.TryDeserialize<List<JsonReference<SpawnData>>>(ref reader, out value2)) { waveData.Spawns = value2; } break; } case "waveheader": waveData.WaveHeader = JsonSerializer.Deserialize<LocaleText>(ref reader, options); break; case "screamsize": waveData.ScreamSize = JsonSerializer.Deserialize<ScreamSize>(ref reader, options); break; case "screamtype": waveData.ScreamType = JsonSerializer.Deserialize<ScreamType>(ref reader, options); break; } } throw new JsonException("Expected EndObject when reading SpawnData object"); } public override void Write(Utf8JsonWriter writer, WaveData? value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); return; } writer.WriteStartObject(); JSON.Serialize(writer, "Spawns", value.Spawns); writer.WriteString("WaveHeader", value.WaveHeader); writer.WriteString("ScreamSize", value.ScreamSize.ToString()); writer.WriteString("ScreamType", value.ScreamType.ToString()); writer.WriteEndObject(); } } public sealed class WaveGroupConverter : JsonConverter<WaveGroupData> { public override WaveGroupData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { WaveGroupData waveGroupData = new WaveGroupData(); if (reader.TokenType != JsonTokenType.StartObject) { throw new JsonException("Expected StartObject when reading WaveGroup"); } while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) { return waveGroupData; } if (reader.TokenType != JsonTokenType.PropertyName) { throw new JsonException("Expected PropertyName when reading WaveGroup"); } string text = reader.GetString().ToLower(); reader.Read(); switch (text) { case "waves": case "wave": { if (reader.TokenType == JsonTokenType.StartArray) { if (!JSON.TryDeserialize<WeightedList<WeightedWaveReference>>(ref reader, out var value3)) { throw new JsonException("Expected list of wave data when reading WaveGroup"); } waveGroupData.Waves = value3; break; } if (!JSON.TryDeserialize<WeightedWaveReference>(ref reader, out var value4)) { throw new JsonException("Expected wave data when reading WaveGroup"); } waveGroupData.Waves = new WeightedList<WeightedWaveReference> { value4 }; break; } case "eventdata": case "events": case "event": { if (reader.TokenType == JsonTokenType.StartArray) { if (!JSON.TryDeserialize<List<JsonReference<WaveEventData>>>(ref reader, out var value)) { throw new JsonException("Expected list of event data when reading WaveGroup"); } waveGroupData.Events = value; break; } if (!JSON.TryDeserialize<JsonReference<WaveEventData>>(ref reader, out var value2)) { throw new JsonException("Expected wave data when reading WaveGroup"); } waveGroupData.Events = new List<JsonReference<WaveEventData>> { value2 }; break; } } } throw new JsonException("Expected EndObject when reading WaveGroup"); } public override void Write(Utf8JsonWriter writer, WaveGroupData? value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); return; } writer.WriteStartObject(); writer.WritePropertyName("Wave"); if (value.Waves.Count == 1) { JsonSerializer.Serialize(writer, value.Waves[0], options); } else { JsonSerializer.Serialize(writer, value.Waves, options); } writer.WritePropertyName("Event"); if (value.Events.Count == 1) { JsonSerializer.Serialize(writer, value.Events[0], options); } else { JsonSerializer.Serialize(writer, value.Events, options); } writer.WriteEndObject(); } } public sealed class WeightedEnemyConverter : JsonConverter<WeightedEnemyData> { public override WeightedEnemyData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using JsonDocument jsonDocument = JsonDocument.ParseValue(ref reader); JsonElement rootElement = jsonDocument.RootElement; WeightedEnemyData weightedEnemyData = new WeightedEnemyData(); if (rootElement.ValueKind == JsonValueKind.Number) { weightedEnemyData.ID = rootElement.GetUInt32(); return weightedEnemyData; } if (rootElement.ValueKind != JsonValueKind.Array) { throw new JsonException("Expected enemy data to be either a number or list"); } JsonElement.ArrayEnumerator arrayEnumerator = rootElement.EnumerateArray(); if (arrayEnumerator.MoveNext()) { weightedEnemyData.ID = arrayEnumerator.Current.GetUInt32(); } if (arrayEnumerator.MoveNext()) { weightedEnemyData.Weight = arrayEnumerator.Current.GetSingle(); } if (arrayEnumerator.MoveNext()) { weightedEnemyData.Cost = arrayEnumerator.Current.GetInt32(); } if (arrayEnumerator.MoveNext()) { throw new JsonException("Expected weighted enemy tuple to be at most 3 elements long"); } return weightedEnemyData; } public override void Write(Utf8JsonWriter writer, WeightedEnemyData? value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); return; } if (value.Weight == 1f && value.Cost == 1) { writer.WriteNumberValue(value.ID); return; } writer.WriteStartArray(); writer.WriteNumberValue(value.ID); writer.WriteNumberValue(value.Weight); writer.WriteNumberValue(value.Cost); writer.WriteEndArray(); } } public sealed class WeightedWaveConverter : JsonConverter<WeightedWaveReference> { public override WeightedWaveReference? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using JsonDocument jsonDocument = JsonDocument.ParseValue(ref reader); JsonElement rootElement = jsonDocument.RootElement; WeightedWaveReference weightedWaveReference = new WeightedWaveReference(); if (rootElement.ValueKind == JsonValueKind.String || rootElement.ValueKind == JsonValueKind.Object) { DeserializeReference(rootElement, weightedWaveReference, options); return weightedWaveReference; } if (rootElement.ValueKind != JsonValueKind.Array) { throw new JsonException("Expected wave data or list of weighted waves"); } JsonElement.ArrayEnumerator arrayEnumerator = rootElement.EnumerateArray(); if (arrayEnumerator.MoveNext()) { DeserializeReference(arrayEnumerator.Current, weightedWaveReference, options); } if (arrayEnumerator.MoveNext()) { weightedWaveReference.Weight = arrayEnumerator.Current.GetSingle(); } if (arrayEnumerator.MoveNext()) { throw new JsonException("Expected weighted wave data to be 2 elements long"); } return weightedWaveReference; } private static void DeserializeReference(JsonElement element, WeightedWaveReference data, JsonSerializerOptions options) { switch (element.ValueKind) { case JsonValueKind.Null: break; case JsonValueKind.String: data.ID = element.Deserialize<string>(options); break; default: data.Value = element.Deserialize<WaveData>(options); break; } } public override void Write(Utf8JsonWriter writer, WeightedWaveReference? value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); return; } if (value.Value != null) { JsonSerializer.Serialize(writer, value.Value, options); return; } if (value.Weight == 1f) { JsonSerializer.Serialize(writer, (JsonReference<WaveData>)value, options); return; } writer.WriteStartArray(); JsonSerializer.Serialize(writer, (JsonReference<WaveData>)value, options); writer.WriteNumberValue(value.Weight); writer.WriteEndArray(); } } } namespace WaveSurvival.Json.Converters.Utils { public class LocaleTextConverter : JsonConverter<LocaleText> { public override bool HandleNull => true; public override LocaleText Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return reader.TokenType switch { JsonTokenType.String => new LocaleText(reader.GetString()), JsonTokenType.Number => new LocaleText(reader.GetUInt32()), JsonTokenType.Null => LocaleText.Empty, _ => throw new JsonException($"LocaleTextJson type: {reader.TokenType} is not implemented!"), }; } public override void Write(Utf8JsonWriter writer, LocaleText value, JsonSerializerOptions options) { if (value.ID != 0) { writer.WriteNumberValue(value.ID); } else { writer.WriteStringValue(value.RawText); } } } public sealed class OptionalListConverter<T> : JsonConverter<List<T>> { public override List<T>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { List<T> list = new List<T>(); if (reader.TokenType == JsonTokenType.StartArray) { while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndArray) { return list; } list.Add(JsonSerializer.Deserialize<T>(ref reader, options)); } throw new JsonException("Expected EndArray when reading list"); } list.Add(JsonSerializer.Deserialize<T>(ref reader, options)); return list; } public override void Write(Utf8JsonWriter writer, List<T>? value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); return; } if (value.Count == 1) { JsonSerializer.Serialize(writer, value[0], options); return; } writer.WriteStartArray(); foreach (T item in value) { JsonSerializer.Serialize(writer, item, options); } writer.WriteEndArray(); } } public sealed class WeightedListConverterFactory : JsonConverterFactory { public override bool CanConvert(Type typeToConvert) { if (!typeToConvert.IsGenericType) { return false; } return typeToConvert.GetGenericTypeDefinition() == typeof(WeightedList<>); } public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) { Type type = typeToConvert.GetGenericArguments()[0]; return (JsonConverter)Activator.CreateInstance(typeof(WeightedListConverter<>).MakeGenericType(type)); } } public sealed class WeightedListConverter<T> : JsonConverter<WeightedList<T>> where T : IWeightable { public override WeightedList<T>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return JsonSerializer.Deserialize<List<T>>(ref reader, options); } public override void Write(Utf8JsonWriter writer, WeightedList<T>? value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); } else { JsonSerializer.Serialize(writer, value.Values, options); } } } } namespace WaveSurvival.Dependencies { internal static class MTFOWrapper { public const string PLUGIN_GUID = "com.dak.MTFO"; } internal static class PartialData { public const string PLUGIN_GUID = "MTFO.Extension.PartialBlocks"; public static readonly bool HasPData; public static JsonConverter PDataIDConverter; static PartialData() { HasPData = ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey("MTFO.Extension.PartialBlocks"); if (HasPData) { SetConverter(); } } public static bool TryGetGUID(string text, out uint guid) { if (!HasPData) { guid = 0u; return false; } return TryGetGUID_Internal(text, out guid); } [MethodImpl(MethodImplOptions.NoInlining)] private static bool TryGetGUID_Internal(string text, out uint guid) { return PersistentIDManager.TryGetId(text, ref guid); } [MethodImpl(MethodImplOptions.NoInlining)] private static void SetConverter() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown PDataIDConverter = (JsonConverter)new PersistentIDConverter(); } } } namespace WaveSurvival.CustomWave { public sealed class ActiveWave { private struct SpawnSettings { public List<(AIG_CourseNode node, LG_Zone zone)>? spawnLocations; public int? spawnDistance; public SpawnSettings(WaveEventData data) { spawnLocations = GetSpawnNodes(data.SpawnLocations); spawnDistance = data.SpawnDistance ?? WaveManager.ActiveObjective.SpawnDistance; } public SpawnSettings(SpawnSettings baseSettings, SpawnData spawnData) { spawnLocations = (List<(AIG_CourseNode node, LG_Zone zone)>?)((spawnData.SpawnLocations != null) ? ((IList)GetSpawnNodes(spawnData.SpawnLocations)) : ((IList)baseSettings.spawnLocations)); spawnDistance = spawnData.SpawnDistance ?? baseSettings.spawnDistance; } } private class SpawnSet { protected Queue<(uint id, int cost)> _spawnQueue = new Queue<(uint, int)>(); public readonly SpawnData Settings; public int RemainingEnemies => _spawnQueue.Count; public bool IsDone => _spawnQueue.Count == 0; public SpawnSet(SpawnData data) { Settings = data; if (Settings.Count > 0) { SetupWeightedSpawns(); } else { SetupUnweightedSpawns(); } } public (uint id, int cost) Dequeue() { return _spawnQueue.Dequeue(); } private void SetupUnweightedSpawns() { foreach (WeightedEnemyData item in (List<WeightedEnemyData>)Settings.Enemies) { _spawnQueue.Enqueue((item.ID, 1)); } } private void SetupWeightedSpawns() { List<WeightedEnemyData> list = Settings.Enemies; float num = list.Sum((WeightedEnemyData data) => data.Weight); int num2 = Settings.Count; while (num2 > 0) { float num3 = WaveManager.Random.NextSingle(num); float num4 = 0f; foreach (WeightedEnemyData item in list) { if (item.Cost > num2) { if (num != num4) { num = num4; break; } DinoLogger.Warning($"Wave spawn unable to use {num2} remaining count (total count: {Settings.Count}, costs: [{string.Join(",", list.ConvertAll((WeightedEnemyData e) => e.Cost))}])!"); num2 = 0; break; } num4 += item.Weight; if (num3 < num4 && num2 >= item.Cost) { _spawnQueue.Enqueue((item.ID, item.Cost)); num2 -= item.Cost; break; } } } } } [CompilerGenerated] private sealed class <DoSpawns>d__30 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public ActiveWave <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DoSpawns>d__30(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_00de: Unknown result type (might be due to invalid IL or missing references) int num = <>1__state; ActiveWave activeWave = <>4__this; SpawnData settings; switch (num) { default: return false; case 0: <>1__state = -1; goto IL_010f; case 1: <>1__state = -1; goto IL_0045; case 2: { <>1__state = -1; goto IL_0101; } IL_010f: if (activeWave.IsDone) { return false; } goto IL_0045; IL_0101: if (activeWave.TryAddSpawns()) { <>2__current = null; <>1__state = 2; return true; } activeWave.IncrementSpawn(); goto IL_010f; IL_0045: if (!activeWave.CanDoSpawn()) { <>2__current = null; <>1__state = 1; return true; } activeWave._lastSubWaveTime = Clock.Time; settings = activeWave._currentSpawn.Settings; if (activeWave.ShouldForceRandomSpawner(settings) || WaveManager.Random.NextSingle() < settings.RandomDirectionChance) { activeWave.SetRandomSpawner(); } else { activeWave.UpdateSpawner(); } foreach (WardenObjectiveEventData item in settings.EventsOnSubWaveStart) { WardenObjectiveManager.CheckAndExecuteEventsOnTrigger(item, (eWardenObjectiveEventTrigger)0, true, 0f); } WaveNetwork.DoWaveScream(settings.SubWaveScreamSize, settings.SubWaveScreamType, activeWave._spawner.Node.Position); goto IL_0101; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <SpawnWave>d__29 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public ActiveWave <>4__this; private IEnumerator <spawns>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <SpawnWave>d__29(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <spawns>5__2 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; ActiveWave activeWave = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; activeWave._lastSubWaveTime = Clock.Time; <spawns>5__2 = activeWave.DoSpawns(); goto IL_0059; case 1: <>1__state = -1; goto IL_0059; case 2: { <>1__state = -1; break; } IL_0059: if (<spawns>5__2.MoveNext()) { <>2__current = null; <>1__state = 1; return true; } break; } if (activeWave.EnemyCount != 0 || activeWave.QueuedCount != 0) { <>2__current = null; <>1__state = 2; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public readonly WaveData Settings; public readonly WaveEventData EventData; private readonly IEnumerator _update; private readonly Queue<SpawnSet> _spawnSetQueue; private SpawnSet? _currentSpawn; private SpawnSettings _currentSpawnSettings; private readonly SpawnSettings _baseSpawnSettings; private float _lastSubWaveTime; private float _nextIntervalTime; private int _intervalCount; private AIG_CourseNode _spawnNode; private EnemySpawner _spawner; public int EnemyCount { get; private set; } public int QueuedCount { get; private set; } [MemberNotNullWhen(false, "_currentSpawn")] private bool IsDone { [MemberNotNullWhen(false, "_currentSpawn")] get { return _currentSpawn == null; } } public ActiveWave(WaveData settings, WaveEventData eventData) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) Settings = settings; EventData = eventData; _baseSpawnSettings = new SpawnSettings(EventData); _update = SpawnWave(); _spawnSetQueue = new Queue<SpawnSet>(); SetupSpawns(); _currentSpawnSettings = _baseSpawnSettings; SetRandomSpawner(); WaveNetwork.DoWaveScream(Settings.ScreamSize, Settings.ScreamType, _spawner.Node.Position); } private void SetupSpawns() { int num = 0; foreach (JsonReference<SpawnData> spawn in Settings.Spawns) { SpawnData spawnData = spawn; SpawnSet spawnSet = new SpawnSet(spawnData); _spawnSetQueue.Enqueue(spawnSet); if (!spawnData.HideFromTotalCount) { num += spawnSet.RemainingEnemies; } } if (!_spawnSetQueue.TryDequeue(out _currentSpawn)) { _currentSpawn = null; } WaveManager.Current.AddWaveEnemyCount(num); } [MemberNotNull(new string[] { "_spawnNode", "_spawner" })] private void SetRandomSpawner() { if (_currentSpawnSettings.spawnLocations != null) { WaveManager.Current.SetRandomSpawnNode(ref _spawnNode, _currentSpawnSettings.spawnLocations); } else { WaveManager.Current.SetRandomSpawnNode(ref _spawnNode); } UpdateSpawner(); } [MemberNotNull("_spawner")] private void UpdateSpawner() { WaveManager.Current.SetRandomSpawner(_spawnNode, ref _spawner, _currentSpawnSettings.spawnDistance); } public static List<(AIG_CourseNode, LG_Zone)>? GetSpawnNodes(List<SpawnPathData>? paths) { if (paths == null || paths.Count == 0) { return null; } List<(AIG_CourseNode, LG_Zone)> list = new List<(AIG_CourseNode, LG_Zone)>(paths.Count); foreach (SpawnPathData path in paths) { if (WaveManager.TryGetNode(path, out AIG_CourseNode node, out LG_Zone zone) && node != null) { list.Add((node, zone)); } } return list; } public bool UpdateCheckDone() { if (_update.MoveNext()) { return false; } return true; } public void OnEnemySpawned(bool hideFromCount) { EnemyCount++; QueuedCount--; WaveManager.Current.OnEnemySpawned(hideFromCount); } public void OnEnemyDead() { EnemyCount--; WaveManager.Current.OnEnemyDead(); } [IteratorStateMachine(typeof(<SpawnWave>d__29))] private IEnumerator SpawnWave() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SpawnWave>d__29(0) { <>4__this = this }; } [IteratorStateMachine(typeof(<DoSpawns>d__30))] private IEnumerator DoSpawns() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DoSpawns>d__30(0) { <>4__this = this }; } private bool CanDoSpawn() { if (IsDone) { return false; } SpawnData settings = _currentSpawn.Settings; if (settings.SubWaveMaxCount > 0 && settings.SubWaveMaxCount <= QueuedCount + EnemyCount) { return false; } if (settings.SubWaveDelay > Clock.Time - _lastSubWaveTime) { return false; } return true; } private bool ShouldForceRandomSpawner(SpawnData spawnData) { List<(AIG_CourseNode, LG_Zone)> spawnLocations = _currentSpawnSettings.spawnLocations; _currentSpawnSettings = new SpawnSettings(_baseSpawnSettings, spawnData); List<(AIG_CourseNode, LG_Zone)> spawnLocations2 = _currentSpawnSettings.spawnLocations; if (spawnLocations == spawnLocations2) { return false; } if (spawnLocations == null || spawnLocations2 == null) { return true; } if (spawnLocations.Count != spawnLocations2.Count) { return true; } for (int i = 0; i < spawnLocations2.Count; i++) { if (spawnLocations[i].Item1.NodeID != spawnLocations2[i].Item1.NodeID) { return true; } } return false; } private void IncrementSpawn() { if (!_spawnSetQueue.TryDequeue(out _currentSpawn)) { _currentSpawn = null; } _intervalCount = 0; _nextIntervalTime = 0f; } private bool TryAddSpawns() { if (_currentSpawn.IsDone) { return false; } if (Clock.Time < _nextIntervalTime) { return true; } SpawnData settings = _currentSpawn.Settings; var (id, num) = _currentSpawn.Dequeue(); _spawner.AddSpawn(id, settings.SpawnRate, settings.HideFromTotalCount, this); QueuedCount++; _intervalCount += num; if (settings.SpawnInterval > 0 && _intervalCount >= settings.SpawnInterval) { _nextIntervalTime = Clock.Time + settings.SpawnDelayOnInterval; _intervalCount -= settings.SpawnInterval; if (WaveManager.Random.NextSingle() < settings.RandomDirectionChanceOnInterval) { SetRandomSpawner(); } else { UpdateSpawner(); } } return !_currentSpawn.IsDone; } } public sealed class EnemySpawner { private static float s_lastSpawnTime; public readonly AIG_CourseNode Node; public readonly int ID; private readonly Placement[] _spawnPositions; private readonly Queue<(uint id, float delay, bool isHidden, ActiveWave wave)> _queuedSpawns = new Queue<(uint, float, bool, ActiveWave)>(); private int _useCount; private int _spawnIndex; public bool Used { get { return _useCount > 0; } set { _useCount += (value ? 1 : (-1)); } } public EnemySpawner(AIG_CourseNode node) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) Node = node; ID = node.NodeID; AIG_NodeCluster nodeCluster = node.m_nodeCluster; _spawnPositions = (Placement[])(object)new Placement[nodeCluster.m_scoredPlacements.Count]; for (int i = 0; i < _spawnPositions.Length; i++) { _spawnPositions[i] = new Placement(nodeCluster.m_scoredPlacements[i].item.Position, EnemyGroup.GetRandomRotation()); } WaveManager.Random.Shuffle(_spawnPositions); } public bool UpdateCheckDone() { float time = Clock.Time; if (_queuedSpawns.TryPeek(out (uint, float, bool, ActiveWave) result) && time >= result.Item2 + s_lastSpawnTime) { _queuedSpawns.Dequeue(); SpawnEnemy(result.Item1, result.Item3, result.Item4); s_lastSpawnTime = time; } if (_queuedSpawns.Count == 0) { return !Used; } return false; } public void AddSpawn(uint id, float spawnRate, bool hideFromCount, ActiveWave wave) { _queuedSpawns.Enqueue((id, 1f / spawnRate, hideFromCount, wave)); } private void SpawnEnemy(uint id, bool hideFromCount, ActiveWave wave) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) Placement val = _spawnPositions[_spawnIndex++]; if (_spawnIndex >= _spawnPositions.Length) { WaveManager.Random.Shuffle(_spawnPositions); _spawnIndex = 0; } EnemyAgent agent = EnemyAllocator.Current.SpawnEnemy(id, Node, (AgentMode)1, val.position, val.rotation, (EnemyGroup)null, -1); wave.OnEnemySpawned(hideFromCount); agent.AddOnDeadOnce(wave.OnEnemyDead); } public void OnCheckpointReload() { _queuedSpawns.Clear(); _useCount = 0; } } public sealed class SpawnPath { private readonly List<(AIG_CourseNode? node, LG_Zone zone)> _path; private int _pathIndex; private int _checkpointIndex; public SpawnPath(List<(AIG_CourseNode? node, LG_Zone zone)> pathList) { _path = pathList; _pathIndex = -1; } public bool TryUpdatePath(out AIG_CourseNode? spawner, out AIG_CourseNode? oldSpawner) { if (_pathIndex != -1 && !ZoneGraphUtil.IsZoneReachable(_path[_pathIndex].zone)) { return TryRevertPath(out spawner, out oldSpawner); } return TryAdvancePath(out spawner, out oldSpawner); } public bool TryRevertPath(out AIG_CourseNode? node, out AIG_CourseNode? oldNode) { if (_pathIndex == -1 || ZoneGraphUtil.IsZoneReachable(_path[_pathIndex].zone)) { node = null; oldNode = null; return false; } int num = _pathIndex; while (num - 1 >= 0 && !ZoneGraphUtil.IsZoneReachable(_path[num - 1].zone)) { num--; } oldNode = _path[_pathIndex].node; _pathIndex = num; if (_pathIndex >= 0) { node = _path[num].node; } else { node = null; } return true; } public bool TryAdvancePath(out AIG_CourseNode? node, out AIG_CourseNode? oldNode) { int i; for (i = _pathIndex; i + 1 < _path.Count && ZoneGraphUtil.IsZoneReachable(_path[i + 1].zone); i++) { } if (i == _pathIndex) { oldNode = null; node = null; return false; } if (_pathIndex >= 0) { oldNode = _path[_pathIndex].node; } else { oldNode = null; } _pathIndex = i; node = _path[i].node; return true; } public void StoreCheckpoint() { _checkpointIndex = _pathIndex; } public bool OnCheckpointReload(out AIG_CourseNode? node) { _pathIndex = _checkpointIndex; if (_pathIndex < 0) { node = null; return false; } node = _path[_pathIndex].node; return true; } } public sealed class WaveManager : MonoBehaviour { private struct CheckpointData { public bool active; public WaveObjectiveData objective; public List<KeyValuePair<eDimensionIndex, WaveObjectiveData>> dimensionStartData; public List<WaveStep> waves; public int currentWave; } private class SpawnNode { public readonly AIG_CourseNode Node; private int _validStack; public void AddUser() { _validStack++; } public bool RemoveUser() { return --_validStack == 0; } public SpawnNode(AIG_CourseNode node) { Node = node; _validStack = 0; } } private struct SearchInfo { public AIG_CourseNode node; public int nodeDist; public int lateralDist; } private readonly struct WaveStep { public readonly WaveData Wave; public readonly WaveEventData EventData; public WaveStep(WaveData wave, WaveEventData eventData) { Wave = wave; EventData = eventData; } } public static readonly Random Random = new Random(); private CheckpointData _checkpointData; private int _enemyCount; private readonly Dictionary<eDimensionIndex, WaveObjectiveData> _dimensionStartData = new Dictionary<eDimensionIndex, WaveObjectiveData>(); private readonly Dictionary<eWardenObjectiveEventType, WaveObjectiveData> _eventStartData = new Dictionary<eWardenObjectiveEventType, WaveObjectiveData>(); private readonly Dictionary<eWardenObjectiveEventType, WaveObjectiveData> _eventStopData = new Dictionary<eWardenObjectiveEventType, WaveObjectiveData>(); private readonly List<SpawnPath> _spawnPaths = new List<SpawnPath>(); private readonly Dictionary<int, EnemySpawner> _activeSpawners = new Dictionary<int, EnemySpawner>(); private readonly Dictionary<int, EnemySpawner> _allSpawners = new Dictionary<int, EnemySpawner>(); private readonly List<EnemySpawner> _finishedSpawners = new List<EnemySpawner>(); private readonly Dictionary<int, SpawnNode> _pathNodes = new Dictionary<int, SpawnNode>(); private readonly Queue<SearchInfo> _searchQueue = new Queue<SearchInfo>(); private const int LateralSearchCap = 2; private readonly List<WaveStep> _waves = new List<WaveStep>(); private readonly Dictionary<int, ActiveWave> _activeWaves = new Dictionary<int, ActiveWave>(); private readonly List<(int, ActiveWave)> _finishedWaves = new List<(int, ActiveWave)>(); private float _nextWaveTime; private float _nextWaveDelayCache; private int _currentWave = -1; public static WaveManager Current { get; private set; } = null; public static bool IsActive { get; private set; } = false; public static bool IsMaster { get; private set; } = false; public static WaveObjectiveData? ActiveObjective { get; private set; } = null; public WaveManager(IntPtr ptr) : base(ptr) { } [InvokeOnLoad] private static void OnLoad() { ClassInjector.RegisterTypeInIl2Cpp<WaveManager>(); } [InvokeOnAssetsLoaded] private static void OnAssetsLoaded() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown GameObject val = new GameObject("WaveSurvival_WaveManager"); Object.DontDestroyOnLoad((Object)val); Current = val.AddComponent<WaveManager>(); IsActive = false; } public static bool TryGetActiveObjective([MaybeNullWhen(false)] out WaveObjectiveData objective) { objective = ActiveObjective; if (IsActive) { return IsMaster; } return false; } private void Update() { if (!IsMaster || !IsActive) { WaveText.Current.Update(); return; } UpdateWaves(); UpdateSpawners(); WaveText.Current.Update(); } [InvokeOnEnter] private static void OnEnter() { IsMaster = SNet.IsMaster; Current.StartObjective(); } private void StartObjective() { if (ActiveObjective != null && !IsActive && IsMaster) { WaveNetwork.SendObjective(ActiveObjective); IsActive = true; SetupSpawners(); SetupWaves(); } } private void FinishObjective() { WaveText.Current.Update(); Cleanup(); } [InvokeOnCleanup] private static void OnCleanup() { Current.Cleanup(); } private void Cleanup(bool isCheckpoint = false) { CleanupWaves(); CleanupSpawners(isCheckpoint); CleanupEnemyCount(); ActiveObjective = null; IsActive = false; } internal static void Internal_ReceiveObjectiveComplete() { Current.FinishObjective(); } internal static void Internal_ReceiveObjectiveID(int id) { if (DataManager.TryGetObjective(id, out WaveObjectiveData objective)) { ActiveObjective = objective; IsActive = true; WaveText.Current.UpdateWaveHeader(); } } internal static void Internal_OnReloadFile(List<WaveObjectiveData> dataList) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Current == (Object)null) { return; } if (TryGetActiveObjective(out WaveObjectiveData objective)) { foreach (WaveObjectiveData data in dataList) { if (data.Level.IsMatch(objective.Level) && data.RundownID == objective.RundownID && data.StartEvent == objective.StartEvent && data.DimensionIndex == objective.DimensionIndex) { ActiveObjective = data; break; } } } Current.LoadLevelObjectives(); } internal static void Internal_OnCheckpointReached() { Current.OnCheckpointReached(); } private void OnCheckpointReached() { _checkpointData.active = IsActive; if (TryGetActiveObjective(out WaveObjectiveData objective)) { _checkpointData.objective = objective; CheckpointStoreObjectives(); CheckpointStoreWaves(); CheckpointStoreSpawners(); } } internal static void Internal_OnCheckpointReload() { Current.OnCheckpointReload(); } private void OnCheckpointReload() { Current.Cleanup(isCheckpoint: true); if (IsMaster && _checkpointData.active) { ActiveObjective = _checkpointData.objective; WaveNetwork.SendObjective(ActiveObjective); IsActive = true; CheckpointReloadObjectives(); CheckpointReloadWaves(); CheckpointReloadSpawners(); } } public void AddWaveEnemyCount(int count) { WaveNetwork.SetEnemyCount(_enemyCount += count); } public void OnEnemySpawned(bool hideFromCount) { if (hideFromCount) { WaveNetwork.SetEnemyCount(++_enemyCount); } } public void OnEnemyDead() { WaveNetwork.SetEnemyCount(--_enemyCount); } private void CleanupEnemyCount() { _enemyCount = 0; } private void LoadLevelObjectives() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) uint num = default(uint); if (RundownManager.ActiveExpedition == null || !RundownManager.TryGetIdFromLocalRundownKey(RundownManager.ActiveRundownKey, ref num)) { return; } pActiveExpedition activeExpeditionData = RundownManager.GetActiveExpeditionData(); uint levelLayoutData = RundownManager.ActiveExpedition.LevelLayoutData; _dimensionStartData.Clear(); _eventStartData.Clear(); _eventStopData.Clear(); foreach (WaveObjectiveData objectiveData in DataManager.ObjectiveDatas) { if ((objectiveData.RundownID != 0 && objectiveData.RundownID != num) || !objectiveData.Level.IsMatch(levelLayoutData, activeExpeditionData.tier, activeExpeditionData.expeditionIndex)) { continue; } if ((int)objectiveData.StartEvent != 0) { if (!_eventStartData.TryAdd(objectiveData.StartEvent, objectiveData)) { DinoLogger.Error($"Unable to register data with StartEvent {objectiveData.StartEvent} (already in use)"); } } else if (!_dimensionStartData.TryAdd(objectiveData.DimensionIndex, objectiveData)) { DinoLogger.Error($"Unable to register data with no start event and DimensionIndex {objectiveData.DimensionIndex} (already in use)"); } if ((int)objectiveData.StopEvent != 0 && !_eventStopData.TryAdd(objectiveData.StopEvent, objectiveData)) { DinoLogger.Error($"Unable to register data with StopEvent {objectiveData.StopEvent} (already in use)"); } } } [InvokeOnBuildStart] private static void OnBuildStart() { Current.LoadLevelObjectives(); if (Current._dimensionStartData.Remove((eDimensionIndex)0, out WaveObjectiveData value)) { ActiveObjective = value; } } private void CheckpointStoreObjectives() { _checkpointData.dimensionStartData = new List<KeyValuePair<eDimensionIndex, WaveObjectiveData>>(_dimensionStartData); } private void CheckpointReloadObjectives() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) _dimensionStartData.Clear(); foreach (KeyValuePair<eDimensionIndex, WaveObjectiveData> dimensionStartDatum in _checkpointData.dimensionStartData) { _dimensionStartData.Add(dimensionStartDatum.Key, dimensionStartDatum.Value); } } internal static void Internal_OnWarp(eDimensionIndex dimensionIndex) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (IsMaster && !IsActive && Current._dimensionStartData.Remove(dimensionIndex, out WaveObjectiveData value)) { ActiveObjective = value; Current.StartObjective(); } } internal static void Internal_OnWardenEvent(eWardenObjectiveEventType type) { //IL_0000: 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_007f: Unknown result type (might be due to invalid IL or missing references) if ((int)type == 0) { return; } IsMaster = SNet.IsMaster; if (IsMaster) { if (IsActive && Current._eventStopData.TryGetValue(type, out WaveObjectiveData value) && value.NetworkID == ActiveObjective?.NetworkID) { WaveNetwork.SetWavesFinished(); } if (!IsActive && Current._eventStartData.TryGetValue(type, out value)) { ActiveObjective = value; Current.StartObjective(); } } } [HideFromIl2Cpp] public void SetRandomSpawnNode([NotNull] ref AIG_CourseNode? node) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (_pathNodes.Count > 0) { node = _pathNodes.Values.ToArray()[Random.Next(_pathNodes.Count)].Node; return; } DinoLogger.Error("No path nodes available! Defaulting to spawn!"); Dimension val = default(Dimension); if (!Builder.CurrentFloor.GetDimension(ActiveObjective.DimensionIndex, ref val)) { node = AIG_CourseNode.s_allNodes[0]; } else { node = val.MainLayer.m_zones[0].m_areas[0].m_courseNode; } } [HideFromIl2Cpp] public void SetRandomSpawnNode([NotNull] ref AIG_CourseNode? node, List<(AIG_CourseNode, LG_Zone)> spawnLocations) { int num = 0; AIG_CourseNode[] array = (AIG_CourseNode[])(object)new AIG_CourseNode[spawnLocations.Count]; foreach (var spawnLocation in spawnLocations) { var (val, _) = spawnLocation; if (ZoneGraphUtil.IsZoneReachable(spawnLocation.Item2)) { array[num++] = val; } } if (num > 0) { node = array[Random.Next(num)]; return; } DinoLogger.Error("No spawn locations reachable! Reverting to path..."); SetRandomSpawnNode(ref node); } [HideFromIl2Cpp] public void SetRandomSpawner(AIG_CourseNode node, [NotNull] ref EnemySpawner? spawner, int? targetNodeDistance = null) { if (spawner != null) { bool flag = spawner.ID == node.NodeID; if (!targetNodeDistance.HasValue && flag) { return; } int nodeDistanceToClosestPlayer = spawner.Node.m_playerCoverage.GetNodeDistanceToClosestPlayer(); if (nodeDistanceToClosestPlayer == targetNodeDistance || (flag && nodeDistanceToClosestPlayer < targetNodeDistance)) { return; } spawner.Used = false; } spawner = GetSpawnerFromNode(node, targetNodeDistance); _activeSpawners.TryAdd(spawner.ID, spawner); spawner.Used = true; } private void UpdateSpawners() { foreach (EnemySpawner value in _activeSpawners.Values) { if (value.UpdateCheckDone()) { _finishedSpawners.Add(value); } } foreach (EnemySpawner finishedSpawner in _finishedSpawners) { _activeSpawners.Remove(finishedSpawner.ID); } _finishedSpawners.Clear(); } private void SetupSpawners() { //IL_000a: 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) Dimension val = default(Dimension); if (!Builder.CurrentFloor.GetDimension(ActiveObjective.DimensionIndex, ref val)) { DinoLogger.Error($"Unable to get dimension {ActiveObjective.DimensionIndex}, no spawners created!"); return; } foreach (List<SpawnPathData> spawnPath2 in ActiveObjective.SpawnPaths) { int num = -1; List<(AIG_CourseNode, LG_Zone)> list = new List<(AIG_CourseNode, LG_Zone)>(spawnPath2.Count); foreach (SpawnPathData item in spawnPath2) { if (TryGetNode(item, out AIG_CourseNode node, out LG_Zone zone) && num <= zone.ID) { num = zone.ID; list.Add((node, zone)); } } SpawnPath spawnPath = new SpawnPath(list); _spawnPaths.Add(spawnPath); if (spawnPath.TryAdvancePath(out AIG_CourseNode node2, out AIG_CourseNode _) && node2 != null) { AddSpawnNode(node2); } } } [HideFromIl2Cpp] public static bool TryGetNode(SpawnPathData data, out AIG_CourseNode? node, [MaybeNullWhen(false)] out LG_Zone zone) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) Dimension val = default(Dimension); if (!Builder.CurrentFloor.GetDimension(ActiveObjective.DimensionIndex, ref val)) { DinoLogger.Error($"Unable to get dimension {ActiveObjective.DimensionIndex}, no spawners created!"); node = null; zone = null; return false; } LG_Layer layer = val.GetLayer(data.Layer); if (layer == null || !layer.m_zonesByLocalIndex.ContainsKey(data.ZoneIndex)) { DinoLogger.Error($"Unable to get zone for index {data.ZoneIndex}, layer {data.Layer}!"); node = null; zone = null; return false; } zone = layer.m_zonesByLocalIndex[data.ZoneIndex]; if (data.AreaIndex == -999) { node = null; return true; } int num = ((data.AreaIndex >= 0) ? data.AreaIndex : (zone.m_areas.Count - 1)); if (num < 0 || num >= zone.m_areas.Count) { DinoLogger.Error($"Unable to get area index {num} for zone index {data.ZoneIndex}, layer {data.Layer} (only {zone.m_areas.Count} areas exist!)"); node = null; zone = null; return false; } node = zone.m_areas[num].m_courseNode; return true; } [HideFromIl2Cpp] public EnemySpawner GetSpawnerFromNode(AIG_CourseNode node, int? targetNodeDistance = null) { int num = 0; if (targetNodeDistance.HasValue) { num = node.m_playerCoverage.GetNodeDistanceToClosestPlayer(); } if (targetNodeDistance.HasValue && num > targetNodeDistance) { List<AIG_CourseNode> list = new List<AIG_CourseNode> { node }; AIG_SearchID.IncrementSearchID(); ushort num2 = (((AIG_CourseGraphMember)node).m_searchID = AIG_SearchID.SearchID); _searchQueue.Enqueue(new SearchInfo { node = node, nodeDist = num, lateralDist = 2 }); SearchInfo result; while (_searchQueue.TryDequeue(out result)) { Enumerator<AIG_CoursePortal> enumerator = result.node.m_portals.GetEnumerator(); while (enumerator.MoveNext()) { AIG_CoursePortal current = enumerator.Current; if (((AIG_CourseGraphMember)current).m_searchID == num2 || current.IsProgressionLocked) { continue; } ((AIG_CourseGraphMember)current).m_searchID = num2; SearchInfo searchInfo = default(SearchInfo); searchInfo.node = current.GetOppositeNode(result.node); SearchInfo item = searchInfo; if (((AIG_CourseGraphMember)item.node).m_searchID == num2) { continue; } ((AIG_CourseGraphMember)item.node).m_searchID = num2; if (!item.node.IsValid) { continue; } item.nodeDist = item.node.m_playerCoverage.GetNodeDistanceToClosestPlayer(); if (item.nodeDist > result.nodeDist || item.nodeDist < targetNodeDistance) { continue; } if (item.nodeDist == result.nodeDist) { if (result.lateralDist >= 2) { continue; } item.lateralDist = result.lateralDist + 1; } else { item.lateralDist = 0; } if (item.nodeDist < num) { list.Clear(); num = item.nodeDist; list.Add(item.node); } else if (item.nodeDist == num) { list.Add(item.node); } _searchQueue.Enqueue(item); } } _searchQueue.Clear(); node = ((list.Count > 1) ? list[Random.Next(list.Count)] : list[0]); } if (!_allSpawners.TryGetValue(node.NodeID, out EnemySpawner value)) { _allSpawners.Add(node.NodeID, value = new EnemySpawner(node)); } return value; } [HideFromIl2Cpp] private void AddSpawnNode(AIG_CourseNode node) { if (!_pathNodes.TryGetValue(node.NodeID, out SpawnNode value)) { _pathNodes.Add(node.NodeID, value = new SpawnNode(node)); } value.AddUser(); } [HideFromIl2Cpp] private void RemoveSpawnNode(AIG_CourseNode node) { if (_pathNodes[node.NodeID].RemoveUser()) { _pathNodes.Remove(node.NodeID); } } private void CheckpointStoreSpawners() { foreach (SpawnPath spawnPath in _spawnPaths) { spawnPath.StoreCheckpoint(); } } private void CheckpointReloadSpawners() { foreach (SpawnPath spawnPath in _spawnPaths) { if (spawnPath.OnCheckpointReload(out AIG_CourseNode node) && node != null) { AddSpawnNode(node); } } foreach (EnemySpawner value in _allSpawners.Values) { value.OnCheckpointReload(); } } private void CleanupSpawners(bool isCheckpoint) { if (!isCheckpoint) { _spawnPaths.Clear(); _allSpawners.Clear(); } _activeSpawners.Clear(); _pathNodes.Clear(); } private void OnReachableUpdate() { if (!IsMaster || !IsActive) { return; } StringBuilder stringBuilder = new StringBuilder("Paths updated: ["); foreach (SpawnPath spawnPath in _spawnPaths) { if (spawnPath.TryUpdatePath(out AIG_CourseNode spawner, out AIG_CourseNode oldSpawner)) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(7, 4, stringBuilder2); handler.AppendLiteral("("); handler.AppendFormatted((oldSpawner != null) ? ((Object)oldSpawner.m_zone.NavInfo).ToString() : null); handler.AppendFormatted((oldSpawner != null) ? ((Object)oldSpawner.m_area.m_navInfo).ToString() : null); handler.AppendLiteral(" -> "); handler.AppendFormatted((spawner != null) ? ((Object)spawner.m_zone.NavInfo).ToString() : null); handler.AppendFormatted((spawner != null) ? ((Object)spawner.m_area.m_navInfo).ToString() : null); handler.AppendLiteral("),"); stringBuilder2.Append(ref handler); if (spawner != null) { AddSpawnNode(spawner); } if (oldSpawner != null) { RemoveSpawnNode(oldSpawner); } } } stringBuilder.Append(']'); DinoLogger.Log(stringBuilder.ToString()); } private void UpdateWaves() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if ((_nextWaveTime > 0f && Clock.Time >= _nextWaveTime) || (_activeWaves.Count == 0 && Input.GetKeyDown(Configuration.SkipWaveBind))) { StartNextWave(); } foreach (var (item, activeWave2) in _activeWaves) { if (activeWave2.UpdateCheckDone()) { _finishedWaves.Add((item, activeWave2)); } } foreach (var (index, wave) in _finishedWaves) { FinishWave(index, wave); } _finishedWaves.Clear(); } private void StartNextWave() { if (_currentWave >= _waves.Count - 1) { return; } WaveStep waveStep = _waves[++_currentWave]; WaveEventData eventData = waveStep.EventData; _nextWaveTime = ((eventData.TimeToNextOnStart > 0f) ? (Clock.Time + eventData.TimeToNextOnStart) : 0f); WaveNetwork.SetWave(_currentWave, waveStep.Wave.NetworkID, _nextWaveTime, WaveState.Wave); foreach (WardenObjectiveEventData item in eventData.EventsOnWaveStart) { WardenObjectiveManager.CheckAndExecuteEventsOnTrigger(item, (eWardenObjectiveEventTrigger)0, true, 0f); } _activeWaves.Add(_currentWave, new ActiveWave(waveStep.Wave, eventData)); } [HideFromIl2Cpp] private void FinishWave(int index, ActiveWave wave) { WaveEventData eventData = wave.EventData; bool flag = _finishedWaves.Count == _activeWaves.Count; if (eventData.EndOnAllWavesEnd && !flag) { return; } if (_currentWave == index) { _nextWaveDelayCache = eventData.TimeToNextOnEnd; if (!flag) { WaveNetwork.SetWave(_currentWave + 1, GetNetworkID(_currentWave + 1), _nextWaveTime, WaveState.RemainingWaves); } } foreach (WardenObjectiveEventData item in eventData.EventsOnWaveEnd) { WardenObjectiveManager.CheckAndExecuteEventsOnTrigger(item, (eWardenObjectiveEventTrigger)0, true, 0f); } if (eventData.HasAnyGain) { Enumerator<PlayerAgent> enumerator2 = PlayerManager.PlayerAgentsInLevel.GetEnumerator(); while (enumerator2.MoveNext()) { PlayerAgent current = enumerator2.Current; if (eventData.HasAnyAmmoGain) { PlayerBackpackManager.GiveAmmoToPlayer(current.Owner, eventData.MainAmmoGainOnEnd, eventData.SpecialAmmoGainOnEnd, eventData.ToolAmmoGainOnEnd); } if (eventData.HealthGainOnEnd > 0f) { current.GiveHealth(current, eventData.HealthGainOnEnd); } } } _activeWaves.Remove(index); if (flag) { float num = ((_nextWaveDelayCache > 0f) ? (Clock.Time + _nextWaveDelayCache) : 0f); if (_nextWaveTime == 0f) { _nextWaveTime = num; } else { _nextWaveTime = Math.Min(_nextWaveTime, num); } if (_currentWave + 1 == _waves.Count) { WaveNetwork.SetWavesFinished(); } else { WaveNetwork.SetWave(_currentWave + 1, GetNetworkID(_currentWave + 1), _nextWaveTime, WaveState.Transition); } } } private int GetNetworkID(int wave) { if (wave >= _waves.Count) { return -1; } return _waves[wave].Wave.NetworkID; } private void SetupWaves() { foreach (WaveGroupData item in ActiveObjective.WaveSequence) { item.RefillWaves(); int num = 0; while (num < item.Events.Count) { _waves.Add(new WaveStep(item.GetWaveData(), item.Events[num++])); } } _nextWaveTime = Clock.Time + ActiveObjective.StartDelay; _currentWave = ActiveObjective.StartWave - 2; WaveNetwork.SetWave(_currentWave + 1, GetNetworkID(_currentWave + 1), _nextWaveTime, WaveState.Transition); } private void CleanupWaves() { _waves.Clear(); _activeWaves.Clear(); _nextWaveTime = 0f; _nextWaveDelayCache = 0f; _currentWave = -1; } private void CheckpointStoreWaves() { _checkpointData.waves = new List<WaveStep>(_waves); _checkpointData.currentWave = ((_activeWaves.Count == 0) ? (_currentWave + 1) : _currentWave); } private void CheckpointReloadWaves() { _waves.AddRange(_checkpointData.waves); _currentWave = _checkpointData.currentWave - 1; float num = ((_currentWave > 0) ? _waves[_currentWave - 1].EventData.TimeToNextOnEnd : _checkpointData.objective.StartDelay); _nextWaveTime = ((num > 0f) ? (Clock.Time + num) : 0f); WaveNetwork.SetWave(_currentWave + 1, GetNetworkID(_currentWave + 1), _nextWaveTime, WaveState.Transition); } } internal static class WaveNetwork { private class ObjectiveSync : SyncedEvent<int> { public override string GUID => "Objective"; protected override void Receive(int id) { WaveManager.Internal_ReceiveObjectiveID(id); } } private class WaveSync : SyncedEvent<pWaveData> { public override string GUID => "Wave"; protected override void Receive(pWaveData packet) { WaveText.Current.Internal_ReceiveWave(packet.wave, packet.networkID, packet.nextWaveTime, packet.state); if (packet.state == WaveState.Finished) { WaveManager.Internal_ReceiveObjectiveComplete(); } } protected override void ReceiveLocal(pWaveData packet) { Receive(packet); } } private class WaveScreamSync : SyncedEvent<pScreamData> { public override string GUID => "Scream"; protected override void Receive(pScreamData packet) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown CellSoundPlayer val = new CellSoundPlayer(); val.SetSwitch(ENEMY_TYPE.GROUP, packet.screamType.ToSwitch()); val.SetSwitch(ROAR_SIZE.GROUP, packet.screamSize.ToSwitch()); val.SetSwitch(ENVIROMENT.GROUP, SWITCH.COMPLEX); CellSoundPlayerExt.PostWithCleanup(val, EVENTS.PLAY_WAVE_DISTANT_ROAR, packet.position); } protected override void ReceiveLocal(pScreamData packet) { Receive(packet); } } private class EnemyCountSync : SyncedEvent<int> { public override string GUID => "Count"; protected override void Receive(int count) { WaveText.Current.Internal_ReceiveEnemyCount(count); } protected override void ReceiveLocal(int count) { Receive(count); } } private struct pWaveData { public int wave; public int networkID; public float nextWaveTime; public WaveState state; } private struct pScreamData { public ScreamSize screamSize; public ScreamType screamType; public Vector3 position; } private static readonly WaveSync _waveSync = new WaveSync(); private static readonly ObjectiveSync _objectiveSync = new ObjectiveSync(); private static readonly WaveScreamSync _waveScreamSync = new WaveScreamSync(); private static readonly EnemyCountSync _enemyCountSync = new EnemyCountSync(); internal static void SendObjective(WaveObjectiveData data, SNet_Player? player = null) { _objectiveSync.Send(data.NetworkID, player, (SNet_ChannelType)3); } internal static void SetWavesFinished() { SetWave(0, 0, 0f, WaveState.Finished); } internal static void SetWave(int wave, int networkID, float nextWaveTime, WaveState waveStep) { _waveSync.Send(new pWaveData { wave = wave, networkID = networkID, nextWaveTime = nextWaveTime, state = waveStep }, null, (SNet_ChannelType)3); } internal static void SetEnemyCount(int enemyCount) { _enemyCountSync.Send(enemyCount, null, (SNet_ChannelType)4); } internal static void DoWaveScream(ScreamSize screamSize, ScreamType screamType, Vector3 position) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (screamType != ScreamType.None) { _waveScreamSync.Send(new pScreamData { screamSize = screamSize, screamType = screamType, position = position }, null, (SNet_ChannelType)3); } } [InvokeOnLoad] private static void OnLoad() { _waveSync.Setup(); _objectiveSync.Setup(); _waveScreamSync.Setup(); _enemyCountSync.Setup(); } } public enum WaveState : byte { Wave, RemainingWaves, Transition, Finished } public sealed class WaveText { public static readonly WaveText Current = new WaveText(); private const float HideFinishDelay = 10f; private static PUI_ObjectiveTimer s_waveInfo = null; private static PUI_InteractionPrompt s_intPrompt = null; private static Vector2 s_IntPromptPos; private static Vector2 s_IntPromptPosInWave; private float _nextWaveTime = -1f; private bool _canSkip; private (int wave, int networkID, WaveState state) _waveCache; private float _hideFinishTime; private int _enemyCount; internal static void Internal_Setup(PUI_ObjectiveTimer waveInfo, PUI_InteractionPrompt intPrompt) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) s_waveInfo = waveInfo; ((GuiLayerComp)waveInfo).SetVisible(false, false); s_intPrompt = intPrompt; ((RectTransformComp)s_intPrompt).SetVisible(false); s_intPrompt.SetTimerFill(0f); s_intPrompt.SetMessage(""); s_intPrompt.m_timerAlpha = 0f; s_IntPromptPos = ((RectTransformComp)s_intPrompt).RectTrans.anchoredPosition; s_IntPromptPosInWave = s_IntPromptPos; s_IntPromptPosInWave.y -= 25f; } [InvokeOnCleanup] private static void OnCleanup() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) ((GuiLayerComp)s_waveInfo).SetVisible(false, false); ((RectTransformComp)s_intPrompt).SetVisible(false); s_intPrompt.SetMessage(""); s_intPrompt.SetTimerFill(0f); s_intPrompt.m_timerAlpha = 0f; ((RectTransformComp)s_intPrompt).RectTrans.anchoredPosition = s_IntPromptPos; } internal void Internal_ReceiveWave(int wave, int networkID, float nextWaveTime, WaveState state) { _waveCache = (wave, networkID, state); ((GuiLayerComp)s_waveInfo).SetVisible(true, false); ((RectTransformComp)s_intPrompt).SetVisible(state == WaveState.Wave || state == WaveState.RemainingWaves); _nextWaveTime = nextWaveTime; _canSkip = state == WaveState.Transition; UpdateWaveHeader(); } internal void Internal_ReceiveEnemyCount(int count) { if (_enemyCount != count) { s_intPrompt.SetMessage($"Enemies Remaining: <color=orange>{count}</color>"); } _enemyCount = count; } public void UpdateWaveHeader() { //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) if (WaveManager.ActiveObjective == null) { return; } if (_waveCache.state == WaveState.Finished) { ((TMP_Text)s_waveInfo.m_titleText).SetText((string)WaveManager.ActiveObjective.CompleteHeader, true); _nextWaveTime = 0f; if (_hideFinishTime <= 0f) { _hideFinishTime = Clock.Time + 10f; } return; } _hideFinishTime = 0f; if (DataManager.TryGetWave(_waveCache.networkID, out WaveData wave)) { if (_waveCache.state != 0) { ((TMP_Text)s_waveInfo.m_titleText).SetText("<u>Next Wave</u><color=orange>\n{0:0}</co