Decompiled source of WaveSurvival v1.0.1
WaveSurvival.dll
Decompiled a week 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.Json; using System.Text.Json.Serialization; using AIGraph; using AK; using Agents; 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 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+edef27c729c591e17dd842fee3bbf1c9653150d9")] [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.0.0")] [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.0.0"; 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 DoorPatches { [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyWrapSafe] [HarmonyPrefix] private static void OnDoorStateChanged(LG_Gate __instance, ref bool __state) { __state = __instance.IsTraversable; } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyWrapSafe] [HarmonyPostfix] private static void OnDoorStateChanged(LG_Gate __instance, bool __state) { if (__instance.IsTraversable != __state) { iLG_Door_Core spawnedDoor = __instance.SpawnedDoor; if (!((Object)(object)((spawnedDoor != null) ? ((Il2CppObjectBase)spawnedDoor).TryCast<LG_SecurityDoor>() : null) == (Object)null)) { ZoneTree.Internal_OnDoorStateChanged(__instance, !__state); } } } } [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>()); } 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 "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()); 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) { throw new JsonException("Expected area index when reading a SpawnPath object"); } spawnPathData.AreaIndex = reader.GetInt32(); 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 if (value == null) { writer.WriteNullValue(); return; } if (value.AreaIndex == -1) { writer.WriteNumberValue((int)value.ZoneIndex); return; } writer.WriteStartArray(); writer.WriteNumberValue((int)value.ZoneIndex); writer.WriteNumberValue(value.AreaIndex); 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; static PartialData() { HasPData = ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey("MTFO.Extension.PartialBlocks"); } 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); } } } namespace WaveSurvival.CustomWave { public sealed class ActiveWave { 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__23 : 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__23(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_00d7: 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_0108; case 1: <>1__state = -1; goto IL_0045; case 2: { <>1__state = -1; goto IL_00fa; } IL_0108: if (activeWave.IsDone) { return false; } goto IL_0045; IL_0045: if (!activeWave.CanDoSpawn()) { <>2__current = null; <>1__state = 1; return true; } activeWave._lastSubWaveTime = Clock.Time; settings = activeWave._currentSpawn.Settings; if (WaveManager.Random.NextSingle() < settings.RandomDirectionChance) { WaveManager.Current.SetRandomSpawner(ref activeWave._spawner); } 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_00fa; IL_00fa: if (activeWave.TryAddSpawns()) { <>2__current = null; <>1__state = 2; return true; } activeWave.IncrementSpawn(); goto IL_0108; } } 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__22 : 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__22(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 float _lastSubWaveTime; private float _nextIntervalTime; private int _intervalCount; 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_0062: Unknown result type (might be due to invalid IL or missing references) Settings = settings; EventData = eventData; _update = SpawnWave(); _spawnSetQueue = new Queue<SpawnSet>(); SetupSpawns(); WaveManager.Current.SetRandomSpawner(ref _spawner); 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); } 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__22))] private IEnumerator SpawnWave() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SpawnWave>d__22(0) { <>4__this = this }; } [IteratorStateMachine(typeof(<DoSpawns>d__23))] private IEnumerator DoSpawns() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DoSpawns>d__23(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 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 (_intervalCount >= settings.SpawnInterval) { _nextIntervalTime = Clock.Time + settings.SpawnDelayOnInterval; _intervalCount -= settings.SpawnInterval; if (WaveManager.Random.NextSingle() < settings.RandomDirectionChanceOnInterval) { WaveManager.Current.SetRandomSpawner(ref _spawner); } } return !_currentSpawn.IsDone; } } public sealed class EnemySpawner { private static float s_lastSpawnTime; public readonly AIG_CourseNode Node; public readonly ZoneNode ZoneNode; 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 _validCount; private int _spawnIndex; public bool Valid { get { return _validCount > 0; } set { _validCount += (value ? 1 : (-1)); } } public bool Used { get { return _useCount > 0; } set { _useCount += (value ? 1 : (-1)); } } public EnemySpawner(AIG_CourseNode node) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) Node = node; ZoneNode = ZoneTree.GetZoneNode(node.m_zone); 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 && !Valid) { 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 sealed class SpawnPath { private readonly List<EnemySpawner> _path; private int _pathIndex; public SpawnPath(List<EnemySpawner> pathList) { _path = pathList; _pathIndex = -1; } public bool TryUpdatePath(bool opened, out EnemySpawner? spawner, out EnemySpawner? oldSpawner) { if (!opened) { return TryRevertPath(out spawner, out oldSpawner); } return TryAdvancePath(out spawner, out oldSpawner); } public bool TryRevertPath(out EnemySpawner? spawner, [MaybeNullWhen(false)] out EnemySpawner oldSpawner) { if (_pathIndex == -1 || _path[_pathIndex].ZoneNode.IsReachable) { spawner = null; oldSpawner = null; return false; } int num = _pathIndex; while (num - 1 >= 0 && !_path[num - 1].ZoneNode.IsReachable) { num--; } oldSpawner = _path[_pathIndex]; oldSpawner.Valid = false; _pathIndex = num; if (_pathIndex >= 0) { spawner = _path[num]; spawner.Valid = true; } else { spawner = null; } return true; } public bool TryAdvancePath([MaybeNullWhen(false)] out EnemySpawner spawner, out EnemySpawner? oldSpawner) { int i; for (i = _pathIndex; i + 1 < _path.Count && _path[i + 1].ZoneNode.IsReachable; i++) { } if (i == _pathIndex) { oldSpawner = null; spawner = null; return false; } if (_pathIndex >= 0) { oldSpawner = _path[_pathIndex]; oldSpawner.Valid = false; } else { oldSpawner = null; } _pathIndex = i; spawner = _path[i]; spawner.Valid = true; 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 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> _validSpawners = new Dictionary<int, EnemySpawner>(); private readonly List<EnemySpawner> _finishedSpawners = new List<EnemySpawner>(); 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() { CleanupWaves(); CleanupSpawners(); 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(); } } internal static void Internal_OnCheckpointReload() { Current.OnCheckpointReload(); } private void OnCheckpointReload() { Current.Cleanup(); if (IsMaster && _checkpointData.active) { ActiveObjective = _checkpointData.objective; WaveNetwork.SendObjective(ActiveObjective); IsActive = true; SetupSpawners(); CheckpointReloadObjectives(); CheckpointReloadWaves(); } } 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 SetRandomSpawner([NotNull] ref EnemySpawner? spawner) { if (spawner != null) { spawner.Used = false; } spawner = _validSpawners.Values.ToArray()[Random.Next(_validSpawners.Count)]; 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) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: 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) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: 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; } Dictionary<int, EnemySpawner> dictionary = new Dictionary<int, EnemySpawner>(); foreach (List<SpawnPathData> spawnPath2 in ActiveObjective.SpawnPaths) { List<EnemySpawner> list = new List<EnemySpawner>(spawnPath2.Count); foreach (SpawnPathData item in spawnPath2) { LG_Layer layer = val.GetLayer(item.Layer); if (layer == null || !layer.m_zonesByLocalIndex.ContainsKey(item.ZoneIndex)) { DinoLogger.Error($"Unable to get zone for index {item.ZoneIndex}, layer {item.Layer}!"); continue; } LG_Zone val2 = layer.m_zonesByLocalIndex[item.ZoneIndex]; int num = ((item.AreaIndex >= 0) ? item.AreaIndex : (val2.m_areas.Count - 1)); if (num < 0 || num >= val2.m_areas.Count) { DinoLogger.Error($"Unable to get area index {num} for zone index {item.ZoneIndex}, layer {item.Layer} (only {val2.m_areas.Count} areas exist!)"); } else { AIG_CourseNode courseNode = val2.m_areas[num].m_courseNode; if (!dictionary.TryGetValue(courseNode.NodeID, out var value)) { dictionary.Add(courseNode.NodeID, value = new EnemySpawner(courseNode)); } list.Add(value); } } SpawnPath spawnPath = new SpawnPath(list); _spawnPaths.Add(spawnPath); if (spawnPath.TryAdvancePath(out EnemySpawner spawner, out EnemySpawner _)) { AddSpawner(spawner); } } } [HideFromIl2Cpp] private void AddSpawner(EnemySpawner spawner) { _activeSpawners.TryAdd(spawner.ID, spawner); _validSpawners.TryAdd(spawner.ID, spawner); } private void CleanupSpawners() { _spawnPaths.Clear(); _activeSpawners.Clear(); _validSpawners.Clear(); } internal static void Internal_OnZoneTreeUpdate(bool opened) { if (!IsMaster || !IsActive) { return; } foreach (SpawnPath spawnPath in Current._spawnPaths) { if (spawnPath.TryUpdatePath(opened, out EnemySpawner spawner, out EnemySpawner oldSpawner)) { if (spawner != null) { Current.AddSpawner(spawner); } if (oldSpawner != null && !oldSpawner.Valid) { Current._validSpawners.Remove(oldSpawner.ID); } } } } 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}</color>", (float)(_waveCache.wave + 1)); ((RectTransformComp)s_intPrompt).RectTrans.anchoredPosition = s_IntPromptPos; } else { string text = wave.WaveHeader.ToString().Replace("[WAVE]", (_waveCache.wave + 1).ToString()); ((TMP_Text)s_waveInfo.m_titleText).SetText(text, true); ((RectTransformComp)s_intPrompt).RectTrans.anchoredPosition = s_IntPromptPosInWave; } } } public void Update() { float time = Clock.Time; if (_hideFinishTime > 0f && time > _hideFinishTime) { OnCleanup(); _hideFinishTime = 0f; } if (_nextWaveTime > 0f) { ((Behaviour)s_waveInfo.m_timerText).enabled = true; TimeSpan timeSpan = TimeSpan.FromSeconds(_nextWaveTime - time); if (_canSkip) { ((TMP_Text)s_waveInfo.m_timerText).SetText("<size=100%>\nNext Wave In: <color=orange>{0:00}:{1:00}</color></size>\n<color=green><size=60%>[Press 'X' to skip to next wave]</size></color>", (float)timeSpan.Minutes, (float)timeSpan.Seconds); } else { ((TMP_Text)s_waveInfo.m_timerText).SetText("<size=100%>\nNext Wave In: <color=orange>{0:00}:{1:00}</color></size>", (float)timeSpan.Minutes, (float)timeSpan.Seconds); } } else if (_canSkip) { ((Behaviour)s_waveInfo.m_timerText).enabled = true; ((TMP_Text)s_waveInfo.m_timerText).SetText("<color=green><size=60%>[Press 'X' to skip to next wave]</size></color>", true); } else { ((Behaviour)s_waveInfo.m_timerText).enabled = false; } } } public sealed class ZoneNode { public readonly LG_Zone Zone; public readonly ZoneNode? Parent; public readonly List<ZoneNode> Children = new List<ZoneNode>(); public bool IsOpened { get; private set; } public bool IsReachable { get; private set; } public ZoneNode(LG_Zone zone) { Zone = zone; LG_Gate sourceGate = zone.m_sourceGate; object obj; if (sourceGate == null) { obj = null; } else { LG_Area linksFrom = ((LG_ZoneExpander)sourceGate).m_linksFrom; obj = ((linksFrom != null) ? linksFrom.m_zone : null); } LG_Zone val = (LG_Zone)obj; if ((Object)(object)val != (Object)null) { Parent = ZoneTree.GetZoneNode(val); Parent.Children.Add(this); IsOpened = zone.m_sourceGate.IsTraversable; } else { IsOpened = true; } } public void SetOpenAndPropagate(bool opened) { IsOpened = opened; PropagateReachable(Parent?.IsReachable ?? true); } private void PropagateReachable(bool reachable) { reachable &= IsOpened; if (IsReachable != reachable) { foreach (ZoneNode child in Children) { child.PropagateReachable(reachable); } } IsReachable = reachable; } } public sealed class ZoneTree { public static readonly ZoneTree Current = new ZoneTree(); private readonly Dictionary<int, ZoneNode> _zoneToNode = new Dictionary<int, ZoneNode>(); public static bool IsReady { get; private set; } = false; public static ZoneNode GetZoneNode(LG_Zone zone) { return Current._zoneToNode[zone.ID]; } [InvokeOnBuildDone] private static void OnBuildDone() { Current.BuildZoneTree(); } private void BuildZoneTree() { Enumerator<LG_Zone> enumerator = Builder.CurrentFloor.allZones.GetEnumerator(); while (enumerator.MoveNext()) { LG_Zone current = enumerator.Current; _zoneToNode.Add(current.ID, new ZoneNode(current)); } Enumerator<Dimension> enumerator2 = Builder.CurrentFloor.m_dimensions.GetEnumerator(); while (enumerator2.MoveNext()) { List<LG_Zone> zones = enumerator2.Current.MainLayer.m_zones; if (zones.Count > 0) { _zoneToNode[zones[0].ID].SetOpenAndPropagate(opened: true); } } IsReady = true; } [InvokeOnCleanup] private static void OnCleanup() { Current._zoneToNode.Clear(); IsReady = false; } internal static void Internal_OnDoorStateChanged(LG_Gate gate, bool opened) { if (IsReady) { GetZoneNode(((LG_ZoneExpander)gate).m_linksTo.m_zone).SetOpenAndPropagate(opened); WaveManager.Internal_OnZoneTreeUpdate(opened); } } } } namespace WaveSurvival.CustomWaveData { public sealed class DataManager { private class DataFolder<T> where T : new() { public readonly string Dir; private readonly Dictionary<string, Dictionary<string, T>> _fileData = new Dictionary<string, Dictionary<string, T>>(); private readonly Dictionary<string, T> _idMap = new Dictionary<string, T>(); public DataFolder(string dir) { Dir = dir; } public bool TryGetValue(string id, [MaybeNullWhen(false)] out T value) { return _idMap.TryGetValue(id, out value); } public void SetFileData(string filepath, Dictionary<string, T> data) { _fileData[filepath] = data; } public void RemoveFile(string filepath) { _fileData.Remove(filepath); } public void ReloadIDs(Action<T>? onAddData = null) { _idMap.Clear(); foreach (KeyValuePair<string, Dictionary<string, T>> item in _fileData.OrderBy<KeyValuePair<string, Dictionary<string, T>>, string>((KeyValuePair<string, Dictionary<string, T>> kv) => kv.Key, StringComparer.Ordinal)) { item.Deconstruct(out var key, out var value); foreach (KeyValuePair<string, T> item2 in value) { item2.Deconstruct(out key, out var value2); string text = key; T val = value2; if (!_idMap.TryAdd(text, val)) { DinoLogger.Error($"Found duplicate id {text} in {Dir}, skipping..."); } else { onAddData?.Invoke(val); } } } } } public static readonly DataManager Current = new DataManager(); private const string ObjectiveDir = "Main"; private const string DataDir = "Data"; private readonly DataFolder<WaveData> _waveFolder = new DataFolder<WaveData>("Waves"); private readonly DataFolder<SpawnData> _spawnFolder = new DataFolder<SpawnData>("Spawns"); private readonly DataFolder<List<WeightedEnemyData>> _enemyFolder = new DataFolder<List<WeightedEnemyData>>("Enemies"); private readonly DataFolder<WaveEventData> _eventFolder = new DataFolder<WaveEventData>("Events"); private readonly Dictionary<string, List<WaveObjectiveData>> _objectiveFiles = new Dictionary<string, List<WaveObjectiveData>>(); private readonly List<WaveObjectiveData> _objectiveDatas = new List<WaveObjectiveData>(); private readonly List<WaveData> _waveDatas = new List<WaveData>(); public static IReadOnlyList<WaveObjectiveData> ObjectiveDatas => Current._objectiveDatas; public static IReadOnlyList<WaveData> WaveDatas => Current._waveDatas; private static bool Resolve<T>(JsonReference<T> reference, DataFolder<T> dataFolder) where T : new() { if (reference.Value != null) { return true; } if (dataFolder.TryGetValue(reference.ID, out T value)) { reference.Value = value; return true; } DinoLogger.Error($"Unable to resolve ID {reference.ID} for type {typeof(T)}"); return false; } public static bool Resolve(JsonReference<List<WeightedEnemyData>> reference) { return Resolve(reference, Current._enemyFolder); } public static bool Resolve(JsonReference<SpawnData> reference) { return Resolve(reference, Current._spawnFolder); } public static bool Resolve(JsonReference<WaveData> reference) { return Resolve(reference, Current._waveFolder); } public static bool Resolve(JsonReference<WaveEventData> reference) { return Resolve(reference, Current._eventFolder); } public static bool TryGetWave(int networkID, [MaybeNullWhen(false)] out WaveData wave) { if (networkID >= 0 && networkID < Current._waveDatas.Count) { wave = Current._waveDatas[networkID]; return true; } wave = null; return false; } public static bool TryGetObjective(int networkID, [MaybeNullWhen(false)] out WaveObjectiveData objective) { if (networkID >= 0 && networkID < Current._objectiveDatas.Count) { objective = Current._objectiveDatas[networkID]; return true; } objective = null; return false; } private void ObjectiveFileChanged(LiveEditEventArgs e) { LiveEditEventArgs e2 = e; DinoLogger.Warning("Main file " + e2.FileName + " changed"); LiveEdit.TryReadFileContent(e2.FullPath, (Action<string>)delegate(string content) { ReadObjectiveContent(e2.FullPath, content); }); OnReload(); } private void ObjectiveFileRemoved(LiveEditEventArgs e) { DinoLogger.Warning("Main file " + e.FileName + " removed"); _objectiveFiles.Remove(e.FullPath); OnReload(); } private void ReadObjectiveContent(string filepath, string content) { if (!JSON.TryDeserializeSafe<List<WaveObjectiveData>>(content, out var value)) { DinoLogger.Error("Failed to read main file!"); return; } WaveManager.Internal_OnReloadFile(value); _objectiveFiles[filepath] = value; } private LiveEditEventHandler DataFileChanged<T>(DataFolder<T> dataFolder) where T : new() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown DataFolder<T> dataFolder2 = dataFolder; return (LiveEditEventHandler)delegate(LiveEditEventArgs e) { DinoLogger.Warning(dataFolder2.Dir + " file " + e.FileName + " changed"); LiveEdit.TryReadFileContent(e.FullPath, (Action<string>)delegate(string content) { ReadDataContent(e.FullPath, content, dataFolder2); }); OnReload(); }; } private LiveEditEventHandler DataFileRemoved<T>(DataFolder<T> dataFolder) where T : new() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown DataFolder<T> dataFolder2 = dataFolder; return (LiveEditEventHandler)delegate(LiveEditEventArgs e) { DinoLogger.Warning(dataFolder2.Dir + " file " + e.FileName + " removed"); dataFolder2.RemoveFile(e.FullPath); OnReload(); }; } private void ReadDataContent<T>(string filepath, string content, DataFolder<T> dataFolder) where T : new() { if (!JSON.TryDeserializeSafe<Dictionary<string, T>>(content, out var value)) { DinoLogger.Error("Failed to read " + dataFolder.Dir + " file!"); } else { dataFolder.SetFileData(filepath, value); } } private void OnReload() { _waveDatas.Clear(); _enemyFolder.ReloadIDs(); _spawnFolder.ReloadIDs(); _waveFolder.ReloadIDs(delegate(WaveData data) { data.NetworkID = _waveDatas.Count; _waveDatas.Add(data); }); _eventFolder.ReloadIDs(); _objectiveDatas.Clear(); foreach (KeyValuePair<string, List<WaveObjectiveData>> item in _objectiveFiles.OrderBy<KeyValuePair<string, List<WaveObjectiveData>>, string>((KeyValuePair<string, List<WaveObjectiveData>> kv) => kv.Key, StringComparer.Ordinal)) { item.Deconstruct(out var _, out var value); foreach (WaveObjectiveData item2 in value) { item2.NetworkID = _objectiveDatas.Count; _objectiveDatas.Add(item2);