Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of MonitorTakeover v1.0.0
Y4NGZMonitorTakeover.dll
Decompiled 5 hours 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.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityEngine.Video; using Y4NGZCompany.Bootstrap; using Y4NGZCompany.Core; using Y4NGZCompany.Core.Compat; using Y4NGZCompany.Experience.UITheme; using Y4NGZCompany.ShipSystems.MonitorRow; using Y4NGZCompany.ShipSystems.Rendering; using Y4NGZCompany.ShipSystems.Takeover; using Y4NGZCore.Configuration; using Y4NGZCore.Diagnostics; using Y4NGZCore.Lifecycle; using Y4NGZCore.Modules; using Y4NGZCore.Modules.Layout; using Y4NGZCore.Modules.Monitor; using Y4NGZCore.Modules.Net; using Y4NGZCore.Modules.Quota; using Y4NGZCore.Modules.ShipSystems; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("DunGen")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")] [assembly: IgnoresAccessChecksTo("EasyTextEffects")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")] [assembly: IgnoresAccessChecksTo("Unity.XR.Management")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Y4NGZMonitorTakeover")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Y4NGZ monitor takeover: the quota milestone registry and its rewards, milestone unlock gating, the quota announcement, the monitor takeover presentation, and Mask Man")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+80b22fc4dbdb42201cfbaf50386770acb6cd33dd")] [assembly: AssemblyProduct("Y4NGZMonitorTakeover")] [assembly: AssemblyTitle("Y4NGZMonitorTakeover")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Y4NGZCompany.Core { public enum ForcedQuotaCompletionResult { StartedImmediately, QueuedUntilOrbit, HostOnly, InvalidQuota, NotInRound, Busy, Unavailable } public static class QuotaProgressionDebugApi { public static ForcedQuotaCompletionResult ForceCompletedQuota(int completedQuota) { try { return QuotaTakeoverDebugController.ForceCompletedQuota(completedQuota); } catch (Exception arg) { ModuleLog.Takeover.LogWarning((object)$"QuotaProgressionDebugApi.ForceCompletedQuota failed: {arg}"); return ForcedQuotaCompletionResult.Unavailable; } } } public readonly struct QuotaTokenGrantRequest { public readonly int CompletedQuota; public readonly int TokensPerPlayer; public readonly string ActionId; public readonly bool IsReplay; public QuotaTokenGrantRequest(int completedQuota, int tokensPerPlayer, string actionId) : this(completedQuota, tokensPerPlayer, actionId, isReplay: false) { } public QuotaTokenGrantRequest(int completedQuota, int tokensPerPlayer, string actionId, bool isReplay) { CompletedQuota = completedQuota; TokensPerPlayer = tokensPerPlayer; ActionId = actionId ?? string.Empty; IsReplay = isReplay; } } public static class QuotaProgressionApi { public const int TokenGrantProtocolVersion = 1; private static Func<QuotaTokenGrantRequest, bool> _tokenGrantHandler; public static bool IsTokenGrantCapabilityAvailable => _tokenGrantHandler != null; public static void RegisterTokenGrantHandler(Func<QuotaTokenGrantRequest, bool> handler) { _tokenGrantHandler = handler; } public static void UnregisterTokenGrantHandler(Func<QuotaTokenGrantRequest, bool> handler) { if (_tokenGrantHandler == handler) { _tokenGrantHandler = null; } } internal static bool TryGrantTokens(QuotaTokenGrantRequest request) { if (_tokenGrantHandler != null) { return _tokenGrantHandler(request); } return false; } } internal enum TakeoverSoundtrackMode { Off, Replace, Mix } internal readonly struct TakeoverSoundtrackPlanWire { internal readonly TakeoverSoundtrackMode Mode; internal readonly string Source; internal readonly string Hash; internal readonly float Volume; internal static readonly TakeoverSoundtrackPlanWire Off = new TakeoverSoundtrackPlanWire(TakeoverSoundtrackMode.Off, string.Empty, string.Empty, 0f); internal bool IsOff { get { if (Mode != TakeoverSoundtrackMode.Off) { return string.IsNullOrWhiteSpace(Source); } return true; } } internal TakeoverSoundtrackPlanWire(TakeoverSoundtrackMode mode, string source, string hash, float volume) { Mode = mode; Source = source ?? string.Empty; Hash = hash ?? string.Empty; Volume = volume; } } internal readonly struct TakeoverMediaPlanWire { internal readonly int Seed; internal readonly int MaxPlayers; internal readonly string[] Pool; internal readonly string[] Hashes; internal bool HasMultipleSources { get { if (Pool != null) { return Pool.Length > 1; } return false; } } internal TakeoverMediaPlanWire(int seed, int maxPlayers, string[] pool, string[] hashes) { Seed = seed; MaxPlayers = maxPlayers; Pool = pool ?? Array.Empty<string>(); Hashes = hashes ?? Array.Empty<string>(); } } internal readonly struct QuotaTakeoverPayload { internal readonly int Quota; internal readonly string Dialogue; internal readonly string MediaFile; internal readonly string MediaHash; internal readonly int SelectionId; internal readonly string AlarmAudioFile; internal readonly string AlarmAudioHash; internal readonly string MumbleAudioFiles; internal readonly string MumbleAudioHashes; internal QuotaTakeoverPayload(int quota, string dialogue, string mediaFile, string mediaHash, int selectionId) : this(quota, dialogue, mediaFile, mediaHash, selectionId, string.Empty, string.Empty, string.Empty, string.Empty) { } internal QuotaTakeoverPayload(int quota, string dialogue, string mediaFile, string mediaHash, int selectionId, string alarmAudioFile, string alarmAudioHash, string mumbleAudioFiles, string mumbleAudioHashes) { Quota = quota; Dialogue = dialogue ?? string.Empty; MediaFile = mediaFile ?? string.Empty; MediaHash = mediaHash ?? string.Empty; SelectionId = selectionId; AlarmAudioFile = alarmAudioFile ?? string.Empty; AlarmAudioHash = alarmAudioHash ?? string.Empty; MumbleAudioFiles = mumbleAudioFiles ?? string.Empty; MumbleAudioHashes = mumbleAudioHashes ?? string.Empty; } internal QuotaTakeoverPayload WithDialogue(string dialogue, int selectionId) { return new QuotaTakeoverPayload(Quota, dialogue, MediaFile, MediaHash, selectionId, AlarmAudioFile, AlarmAudioHash, MumbleAudioFiles, MumbleAudioHashes); } } internal readonly struct TakeoverAudioOverrideFile { internal readonly string Path; internal readonly string Hash; internal bool IsValid => !string.IsNullOrEmpty(Path); internal string CacheKey => (Path ?? string.Empty) + "|" + (Hash ?? string.Empty); internal TakeoverAudioOverrideFile(string path, string hash) { Path = path ?? string.Empty; Hash = hash ?? string.Empty; } } internal sealed class QuotaMilestoneConfig { internal readonly int Quota; internal readonly ConfigEntry<bool> Enable; internal readonly ConfigEntry<string> Dialogue; internal readonly ConfigEntry<string> DialogueFile; internal readonly ConfigEntry<string> MediaFile; internal readonly ConfigEntry<string> UnlockContracts; internal readonly ConfigEntry<string> UnlockShipUpgradePurchases; internal readonly ConfigEntry<string> UnlockConstellations; internal readonly ConfigEntry<string> UnlockSuits; internal readonly ConfigEntry<string> UnlockStoreItems; internal readonly ConfigEntry<int> GrantTokensPerPlayer; internal readonly ConfigEntry<int> GrantGroupCredits; internal readonly ConfigEntry<float> GrantShipFuel; internal IEnumerable<string> Contracts => Split(UnlockContracts.Value); internal IEnumerable<string> Upgrades => Split(UnlockShipUpgradePurchases.Value); internal IEnumerable<string> Constellations => Split(UnlockConstellations.Value); internal IEnumerable<string> Suits => Split(UnlockSuits.Value); internal IEnumerable<string> StoreItems => Split(UnlockStoreItems.Value); internal QuotaMilestoneConfig(ConfigFile config, int quota) { //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Expected O, but got Unknown //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Expected O, but got Unknown Quota = quota; string text = MonitorTakeoverConfigSchemaMigration.QuotaSection(quota); Enable = config.Bind<bool>(text, "Enable", true, "Enable this quota milestone."); Dialogue = config.Bind<string>(text, "Dialogue", string.Empty, "Inline takeover dialogue. Use \\n for line breaks; blank uses built-in dialogue."); DialogueFile = config.Bind<string>(text, "DialogueFile", string.Empty, "Optional UTF-8 .txt file under BepInEx/config/Y4NGZCompany/MonitorTakeovers."); MediaFile = config.Bind<string>(text, "MediaFile", string.Empty, "Optional PNG, JPG, or MP4 filename in BepInEx/config/Y4NGZCompany/MonitorTakeovers used for this quota's takeover, or a full YouTube link (watch, Shorts, Live, Embed, or youtu.be). Files need an identical copy on every player or that player falls back to the built-in media; a YouTube link is downloaded and cached by each player in the background instead."); UnlockContracts = config.Bind<string>(text, "UnlockContracts", string.Empty, "Comma-separated stable contract IDs that begin rolling at this quota."); UnlockShipUpgradePurchases = config.Bind<string>(text, "UnlockShipUpgradePurchases", string.Empty, "Comma-separated stable ship-upgrade IDs unlocked for purchase at this quota."); UnlockConstellations = config.Bind<string>(text, "UnlockConstellations", string.Empty, "Comma-separated constellation names unlocked at this quota."); UnlockSuits = config.Bind<string>(text, "UnlockSuits", string.Empty, "Comma-separated suit unlockable names (as in StartOfRound.unlockablesList, e.g. Green suit) hidden from the suit rack until this quota."); UnlockStoreItems = config.Bind<string>(text, "UnlockStoreItems", string.Empty, "Comma-separated store item names (as in Terminal.buyableItemsList, e.g. Pro-flashlight) unavailable for purchase until this quota."); GrantTokensPerPlayer = config.Bind<int>(text, "GrantTokensPerPlayer", 3, new ConfigDescription("Tokens granted once per eligible player through Y4NGZUpgrades.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>())); GrantGroupCredits = config.Bind<int>(text, "GrantGroupCredits", 0, new ConfigDescription("Group credits granted once when this quota completes.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100000), Array.Empty<object>())); GrantShipFuel = config.Bind<float>(text, "GrantShipFuel", 0f, new ConfigDescription("Ship fuel granted once when this quota completes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f), Array.Empty<object>())); } private static IEnumerable<string> Split(string value) { return from item in (value ?? string.Empty).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select item.Trim() into item where item.Length > 0 select item; } } internal static class QuotaProgressionRegistry { private sealed class ModuleGate : IQuotaUnlockGate { public bool IsFeatureUnlocked(string category, string stableId, int completedQuotas) { return category switch { "assignment" => IsContractUnlocked(stableId, completedQuotas), "ship_upgrade" => IsShipUpgradePurchaseUnlocked(stableId, completedQuotas), "constellation" => IsConstellationUnlocked(stableId, completedQuotas), "suit" => IsSuitUnlocked(stableId), "store_item" => IsStoreItemUnlocked(stableId), _ => true, }; } public bool TryGetUnlockQuota(string category, string stableId, out int quota) { if (category == "constellation") { return TryGetConstellationUnlockQuota(stableId, out quota); } quota = 0; return false; } } private sealed class ModulePresentationPolicy : IQuotaPresentationPolicy { public bool RewardsCeremonyEnabled => QuotaProgressionRegistry.RewardsCeremonyEnabled; } internal sealed class QuotaMilestoneSummary { internal int Quota; internal bool Enabled; internal IReadOnlyList<string> Contracts; internal IReadOnlyList<string> ShipUpgrades; internal IReadOnlyList<string> Constellations; internal IReadOnlyList<string> Suits; internal IReadOnlyList<string> StoreItems; internal int TokensPerPlayer; internal int GroupCredits; internal float ShipFuel; } [CompilerGenerated] private static class <>O { public static Action<QuotaRolloverFinalized> <0>__OnQuotaRolloverFinalized; public static Action<string> <1>__OnRewardProviderAvailable; public static HandleNamedMessageDelegate <2>__OnPayloadMessage; public static HandleNamedMessageDelegate <3>__OnGateSnapshotMessage; public static HandleNamedMessageDelegate <4>__OnSoundtrackMessage; public static HandleNamedMessageDelegate <5>__OnMediaPlanMessage; public static Action<ulong> <6>__OnClientConnected; public static Func<string, string> <7>__Normalize; public static Func<char, bool> <8>__IsLetterOrDigit; } private const string LedgerSaveKey = "Y4NGZCompany.QuotaProgressionLedger.v1"; private const string PayloadMessage = "Y4NGZCompany.QuotaTakeoverPayload.v2"; private const string GateSnapshotMessage = "Y4NGZCompany.QuotaProgressionSnapshot.v2"; private const string SoundtrackMessage = "Y4NGZCompany.QuotaTakeoverSoundtrack.v1"; private const string MediaPlanMessage = "Y4NGZCompany.QuotaTakeoverMediaPlan.v1"; private const int MaxPerMonitorPoolEntries = 6; private const int MaxPerMonitorPoolChars = 3000; private static readonly Dictionary<int, QuotaMilestoneConfig> Milestones = new Dictionary<int, QuotaMilestoneConfig>(); private static readonly string[] MediaExtensions = new string[4] { ".png", ".jpg", ".jpeg", ".mp4" }; private static readonly string[] AudioExtensions = new string[3] { ".mp3", ".wav", ".ogg" }; private const int MaxOverrideFileNameChars = 120; private const int MaxYoutubeUrlChars = 500; private const int MaxMumbleOverrideClips = 12; private const int MaxMumbleOverrideNameChars = 1000; private const int MaxMumbleOverrideHashChars = 1000; private static ConfigEntry<bool> _enabled; private static ConfigEntry<bool> _retryPending; private static ConfigEntry<bool> _randomizeDialogue; private static ConfigEntry<bool> _randomizeWithoutReplacement; private static ConfigEntry<string> _defaultMediaFile; private static ConfigEntry<string> _mumbleAudioFiles; private static ConfigEntry<string> _alarmAudioFile; private static ConfigEntry<string> _soundtrackSource; private static ConfigEntry<TakeoverSoundtrackMode> _soundtrackMode; private static ConfigEntry<float> _soundtrackVolume; private static ConfigEntry<bool> _loopMedia; private static ConfigEntry<bool> _enableMediaAudio; private static ConfigEntry<bool> _useMediaLengthAsDuration; private static ConfigEntry<bool> _randomPerMonitorMedia; private static ConfigEntry<string> _perMonitorMediaPool; private static ConfigEntry<int> _maxConcurrentMediaPlayers; private static ConfigEntry<bool> _enableUnlockAnnouncement; private static ConfigEntry<float> _unlockAnnouncementSeconds; private static ConfigEntry<bool> _announceOnBothMonitors; private static ConfigEntry<bool> _rewardsCeremonyEnabled; private static ConfigEntry<bool> _ceremonyMirrorBothMonitors; private static ConfigEntry<float> _ceremonyTypewriterSecondsPerChar; private static ConfigEntry<bool> _ceremonyLineConfirmSting; private static ConfigEntry<bool> _ceremonyRestoreStatic; private static ManualLogSource _log; private static CustomMessagingManager _registeredMessaging; private static NetworkManager _registeredNetworkManager; private static QuotaTakeoverPayload _currentPayload; private static bool _hasCurrentPayload; private static TakeoverSoundtrackPlanWire _soundtrackPlan = TakeoverSoundtrackPlanWire.Off; private static TakeoverMediaPlanWire _mediaPlan; private static bool _hasAuthoritativeUpgradeSnapshot; private static readonly HashSet<string> AuthoritativeUnlockedUpgrades = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, int> AuthoritativeConstellationUnlockQuotas = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, int> AuthoritativeSuitUnlockQuotas = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, int> AuthoritativeStoreItemUnlockQuotas = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private static int _authoritativeQuota; private static float _nextSnapshotAt; private static string _activeSaveName = string.Empty; private static string _cachedLedgerSave = string.Empty; private static HashSet<string> _cachedLedger; private static Action<ulong> _clientConnectedHandler; private static float _nextTokenReplayAt; private static int _tokenReplayAttemptsRemaining; private const float TokenReplayFirstDelaySeconds = 6f; private const float TokenReplayIntervalSeconds = 6f; private const int TokenReplayAttempts = 3; private static ModuleGate _moduleGate; private static ModulePresentationPolicy _modulePresentationPolicy; private static bool _rolloverSubscribed; private const string FuelLedgerKind = "fuel"; private const string CreditsLedgerKind = "credits"; internal static string MediaDirectory => Path.Combine(Y4NGZCompanyPaths.LocalDataDir, "MonitorTakeovers"); internal static bool RandomizeDialogue => _randomizeDialogue?.Value ?? false; internal static bool LoopMedia => _loopMedia?.Value ?? true; internal static bool EnableMediaAudio => _enableMediaAudio?.Value ?? false; internal static bool UseMediaLengthAsDuration => _useMediaLengthAsDuration?.Value ?? false; internal static bool EnableUnlockAnnouncement => _enableUnlockAnnouncement?.Value ?? true; internal static float UnlockAnnouncementSeconds => _unlockAnnouncementSeconds?.Value ?? 4.5f; internal static bool AnnounceOnBothMonitors => _announceOnBothMonitors?.Value ?? false; internal static bool RewardsCeremonyEnabled => _rewardsCeremonyEnabled?.Value ?? true; internal static bool CeremonyMirrorBothMonitors => _ceremonyMirrorBothMonitors?.Value ?? true; internal static float CeremonyTypewriterSecondsPerChar => Mathf.Clamp(_ceremonyTypewriterSecondsPerChar?.Value ?? 0.03f, 0.005f, 0.2f); internal static bool CeremonyLineConfirmSting => _ceremonyLineConfirmSting?.Value ?? true; internal static bool CeremonyRestoreStatic => _ceremonyRestoreStatic?.Value ?? true; internal static TakeoverSoundtrackMode SoundtrackMode => _soundtrackMode?.Value ?? TakeoverSoundtrackMode.Off; internal static float SoundtrackVolume => Mathf.Clamp01(_soundtrackVolume?.Value ?? 0.8f); internal static bool RandomPerMonitorMedia => _randomPerMonitorMedia?.Value ?? false; internal static int MaxConcurrentMediaPlayers => Mathf.Clamp(_maxConcurrentMediaPlayers?.Value ?? 3, 1, 6); internal static string[] PerMonitorMediaPool() { return SplitSemicolonList(_perMonitorMediaPool?.Value); } internal static void Initialize(ConfigFile config, ManualLogSource log) { //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Expected O, but got Unknown //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Expected O, but got Unknown //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Expected O, but got Unknown _log = log; _enabled = config.Bind<bool>("General", "EnableQuotaProgression", true, "Enable quota-based unlock gates, rewards, and takeover presentation."); _retryPending = config.Bind<bool>("General", "RetryPendingOptionalRewards", true, "Retry optional rewards such as tokens when their provider becomes available later."); _randomizeDialogue = config.Bind<bool>("Presentation", "RandomizeDialogue", false, "Choose configured dialogue from any enabled quota instead of attaching dialogue to its quota."); _randomizeWithoutReplacement = config.Bind<bool>("Presentation", "RandomizeWithoutReplacement", true, "When randomizing, use every available passage once before the deterministic pool repeats."); _defaultMediaFile = config.Bind<string>("Media", "DefaultMediaFile", string.Empty, "Optional PNG, JPG, or MP4 filename in BepInEx/config/Y4NGZCompany/MonitorTakeovers used when a quota has no MediaFile, or a full YouTube link (watch, Shorts, Live, Embed, or youtu.be). Files need an identical copy on every player or that player falls back to the built-in media; a YouTube link is downloaded and cached by each player in the background instead."); _mumbleAudioFiles = config.Bind<string>("Audio", "MumbleAudioFiles", string.Empty, "Comma-separated MP3, WAV, or OGG filenames in BepInEx/config/Y4NGZCompany/MonitorTakeovers that replace the quota takeover's mumble voice clips. Every player in the lobby needs an identical copy or that player falls back to the built-in audio."); _alarmAudioFile = config.Bind<string>("Audio", "AlarmAudioFile", string.Empty, "Single MP3, WAV, or OGG filename in BepInEx/config/Y4NGZCompany/MonitorTakeovers that replaces the quota takeover's alarm sound. Every player in the lobby needs an identical copy or that player falls back to the built-in audio."); _soundtrackSource = config.Bind<string>("Audio", "SoundtrackSource", string.Empty, "Optional MP3, WAV, or OGG filename in BepInEx/config/Y4NGZCompany/MonitorTakeovers, or a full YouTube link, played as the quota takeover's soundtrack. Files need an identical copy on every player; a YouTube link is downloaded and cached by each player in the background. Ignored when SoundtrackMode is Off."); _soundtrackMode = config.Bind<TakeoverSoundtrackMode>("Audio", "SoundtrackMode", TakeoverSoundtrackMode.Off, "Off keeps the built-in takeover audio. Replace silences the power-down whine, the alarm, the drone bed, and the mumble voices for the whole takeover and plays only the soundtrack. Mix layers the soundtrack over them."); _soundtrackVolume = config.Bind<float>("Audio", "SoundtrackVolume", 0.8f, new ConfigDescription("Soundtrack volume (0 = silent, 1 = full). Independent of Mumble Volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); _loopMedia = config.Bind<bool>("Media", "LoopMedia", true, "Loop configured MP4 media for the duration of the takeover."); _enableMediaAudio = config.Bind<bool>("Media", "EnableMediaAudio", false, "Play the configured MP4 audio track. Disabled by default so it does not compete with takeover speech."); _useMediaLengthAsDuration = config.Bind<bool>("Media", "UseMediaLengthAsDuration", false, "Hold the takeover for the configured video's full length instead of Takeover Duration. Applies only when the takeover media is a video (local MP4 or YouTube link); clamped to 30 minutes."); _randomPerMonitorMedia = config.Bind<bool>("Media", "RandomPerMonitorMedia", false, "Play different media on different ship monitors during the quota takeover. Off keeps every monitor on one shared video."); _perMonitorMediaPool = config.Bind<string>("Media", "PerMonitorMediaPool", string.Empty, "Semicolon-separated list of PNG/JPG/MP4 filenames in BepInEx/config/Y4NGZCompany/MonitorTakeovers and/or full YouTube links used when RandomPerMonitorMedia is on. Empty falls back to the quota's MediaFile and DefaultMediaFile. Files need an identical copy on every player; links are cached by each player. Example: clip1.mp4;https://youtu.be/abc;poster.png"); _maxConcurrentMediaPlayers = config.Bind<int>("Media", "MaxConcurrentMediaPlayers", 3, new ConfigDescription("Maximum simultaneous video decoders during a per-monitor takeover. Extra monitors share the players in a fixed rotation.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6), Array.Empty<object>())); _enableUnlockAnnouncement = config.Bind<bool>("Rewards Ceremony", "EnableUnlockAnnouncement", true, "After the takeover ends, show the quota's new unlocks and grants on the left large ship monitor."); _unlockAnnouncementSeconds = config.Bind<float>("Rewards Ceremony", "UnlockAnnouncementSeconds", 4.5f, new ConfigDescription("Seconds the unlock announcement stays on the monitor.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 15f), Array.Empty<object>())); _announceOnBothMonitors = config.Bind<bool>("Rewards Ceremony", "AnnounceOnBothMonitors", false, "Also show the unlock announcement on the right large ship monitor."); _rewardsCeremonyEnabled = config.Bind<bool>("Rewards Ceremony", "RewardsCeremonyEnabled", true, "Present the unlock announcement as a ceremony: each reward line types out with a tick per step and a confirm when it lands, and the display hold starts after typing finishes."); _ceremonyMirrorBothMonitors = config.Bind<bool>("Rewards Ceremony", "CeremonyMirrorBothMonitors", true, "During the rewards ceremony, mirror the announcement onto the right large ship monitor as well."); _ceremonyTypewriterSecondsPerChar = config.Bind<float>("Rewards Ceremony", "CeremonyTypewriterSecondsPerChar", 0.03f, new ConfigDescription("Seconds each typed character takes during the rewards ceremony.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.005f, 0.2f), Array.Empty<object>())); _ceremonyLineConfirmSting = config.Bind<bool>("Rewards Ceremony", "CeremonyLineConfirmSting", true, "Play a confirm sting as each reward line finishes typing."); _ceremonyRestoreStatic = config.Bind<bool>("Rewards Ceremony", "CeremonyRestoreStatic", true, "End the rewards ceremony with a brief CRT static burst before the monitors return to their normal views instead of a hard cut."); Directory.CreateDirectory(MediaDirectory); Milestones.Clear(); for (int i = 1; i <= 9; i++) { Milestones[i] = new QuotaMilestoneConfig(config, i); } if (_enabled.Value) { YoutubeVideoResolver.Prefetch(_defaultMediaFile?.Value); YoutubeVideoResolver.Prefetch(_soundtrackSource?.Value); foreach (QuotaMilestoneConfig value in Milestones.Values) { if (value.Enable.Value) { YoutubeVideoResolver.Prefetch(value.MediaFile.Value); } } string[] array = PerMonitorMediaPool(); for (int j = 0; j < array.Length; j++) { YoutubeVideoResolver.Prefetch(array[j]); } WarnOnCommaSeparatedMediaPool(); } WriteEffectiveAudit(); EnsureModuleWiring(); } internal static void EnsureModuleWiring() { if (_moduleGate == null) { _moduleGate = new ModuleGate(); _modulePresentationPolicy = new ModulePresentationPolicy(); ModuleCapabilityRegistry.Register("y4ngz.quota.unlocks", (object)_moduleGate); ModuleCapabilityRegistry.Register("y4ngz.quota.presentation_policy", (object)_modulePresentationPolicy); } if (!_rolloverSubscribed) { _rolloverSubscribed = true; QuotaRolloverNotifications.QuotaRolloverFinalized += OnQuotaRolloverFinalized; QuotaRewardBroker.RewardProviderAvailable += OnRewardProviderAvailable; } } private static void OnQuotaRolloverFinalized(QuotaRolloverFinalized rollover) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<bool> enabled = _enabled; if (enabled != null && enabled.Value) { NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && singleton.IsServer) { int num = Math.Max(0, rollover.CompletedQuotaCount); _currentPayload = BuildPayload(num); _hasCurrentPayload = true; SendPayload(_currentPayload); SendSoundtrack(BuildSoundtrackPlan()); SendMediaPlan(BuildMediaPlan(_currentPayload)); ApplyRewardsThroughQuota(num); QuotaProgressionSuitGate.OnUnlockStateMaybeChanged(num); } } } private static void OnRewardProviderAvailable(string rewardKind) { ConfigEntry<bool> enabled = _enabled; if (enabled == null || !enabled.Value) { return; } NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && singleton.IsServer) { int num = (((Object)(object)TimeOfDay.Instance != (Object)null) ? Math.Max(0, TimeOfDay.Instance.timesFulfilledQuota) : 0); if (num >= 1) { ApplyRewardsThroughQuota(num); } } } internal static void Tick() { //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Expected O, but got Unknown //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Expected O, but got Unknown //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Expected O, but got Unknown //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsListening || singleton.CustomMessagingManager == null) { return; } string text = CurrentSaveName(); if (!string.Equals(_activeSaveName, text, StringComparison.Ordinal)) { _activeSaveName = text; _hasCurrentPayload = false; _soundtrackPlan = TakeoverSoundtrackPlanWire.Off; _mediaPlan = default(TakeoverMediaPlanWire); _hasAuthoritativeUpgradeSnapshot = false; AuthoritativeUnlockedUpgrades.Clear(); AuthoritativeConstellationUnlockQuotas.Clear(); AuthoritativeSuitUnlockQuotas.Clear(); AuthoritativeStoreItemUnlockQuotas.Clear(); _authoritativeQuota = 0; _cachedLedger = null; _cachedLedgerSave = string.Empty; _tokenReplayAttemptsRemaining = 0; QuotaRolloverNotifications.ResetRunState(); } CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager; if (customMessagingManager != _registeredMessaging) { if (_registeredMessaging != null) { try { _registeredMessaging.UnregisterNamedMessageHandler("Y4NGZCompany.QuotaTakeoverPayload.v2"); } catch { } try { _registeredMessaging.UnregisterNamedMessageHandler("Y4NGZCompany.QuotaProgressionSnapshot.v2"); } catch { } try { _registeredMessaging.UnregisterNamedMessageHandler("Y4NGZCompany.QuotaTakeoverSoundtrack.v1"); } catch { } try { _registeredMessaging.UnregisterNamedMessageHandler("Y4NGZCompany.QuotaTakeoverMediaPlan.v1"); } catch { } } if ((Object)(object)_registeredNetworkManager != (Object)null && _clientConnectedHandler != null) { try { _registeredNetworkManager.OnClientConnectedCallback -= _clientConnectedHandler; } catch { } } object obj6 = <>O.<2>__OnPayloadMessage; if (obj6 == null) { HandleNamedMessageDelegate val = OnPayloadMessage; <>O.<2>__OnPayloadMessage = val; obj6 = (object)val; } customMessagingManager.RegisterNamedMessageHandler("Y4NGZCompany.QuotaTakeoverPayload.v2", (HandleNamedMessageDelegate)obj6); object obj7 = <>O.<3>__OnGateSnapshotMessage; if (obj7 == null) { HandleNamedMessageDelegate val2 = OnGateSnapshotMessage; <>O.<3>__OnGateSnapshotMessage = val2; obj7 = (object)val2; } customMessagingManager.RegisterNamedMessageHandler("Y4NGZCompany.QuotaProgressionSnapshot.v2", (HandleNamedMessageDelegate)obj7); object obj8 = <>O.<4>__OnSoundtrackMessage; if (obj8 == null) { HandleNamedMessageDelegate val3 = OnSoundtrackMessage; <>O.<4>__OnSoundtrackMessage = val3; obj8 = (object)val3; } customMessagingManager.RegisterNamedMessageHandler("Y4NGZCompany.QuotaTakeoverSoundtrack.v1", (HandleNamedMessageDelegate)obj8); object obj9 = <>O.<5>__OnMediaPlanMessage; if (obj9 == null) { HandleNamedMessageDelegate val4 = OnMediaPlanMessage; <>O.<5>__OnMediaPlanMessage = val4; obj9 = (object)val4; } customMessagingManager.RegisterNamedMessageHandler("Y4NGZCompany.QuotaTakeoverMediaPlan.v1", (HandleNamedMessageDelegate)obj9); _soundtrackPlan = TakeoverSoundtrackPlanWire.Off; _mediaPlan = default(TakeoverMediaPlanWire); _clientConnectedHandler = OnClientConnected; try { singleton.OnClientConnectedCallback += _clientConnectedHandler; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Quota progression client-connect hook failed: " + ex.Message)); } } _registeredMessaging = customMessagingManager; _registeredNetworkManager = singleton; _nextSnapshotAt = 0f; _tokenReplayAttemptsRemaining = 0; if (!singleton.IsServer) { _hasAuthoritativeUpgradeSnapshot = false; } } if (singleton.IsServer && Time.realtimeSinceStartup >= _nextSnapshotAt) { _nextSnapshotAt = Time.realtimeSinceStartup + 5f; SendGateSnapshot(); if ((Object)(object)TimeOfDay.Instance != (Object)null && TimeOfDay.Instance.timesFulfilledQuota > 0 && QuotaRewardLedger.ShouldRetryRewards(_retryPending?.Value ?? false, LoadLedger())) { ApplyRewardsThroughQuota(TimeOfDay.Instance.timesFulfilledQuota); } } if (singleton.IsServer && _tokenReplayAttemptsRemaining > 0 && Time.realtimeSinceStartup >= _nextTokenReplayAt) { _tokenReplayAttemptsRemaining--; _nextTokenReplayAt = Time.realtimeSinceStartup + 6f; ReplayLedgeredTokenGrants(); } } private static void OnClientConnected(ulong clientId) { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsServer && clientId != singleton.LocalClientId) { ConfigEntry<bool> enabled = _enabled; if (enabled != null && enabled.Value) { _tokenReplayAttemptsRemaining = 3; _nextTokenReplayAt = Time.realtimeSinceStartup + 6f; } } } private static void ReplayLedgeredTokenGrants() { ConfigEntry<bool> enabled = _enabled; if (enabled == null || !enabled.Value) { return; } NetworkManager singleton = NetworkManager.Singleton; if (singleton == null || !singleton.IsServer || !QuotaProgressionApi.IsTokenGrantCapabilityAvailable) { return; } int completedQuota = (((Object)(object)TimeOfDay.Instance != (Object)null) ? Math.Max(0, TimeOfDay.Instance.timesFulfilledQuota) : 0); if (completedQuota < 1) { return; } HashSet<string> ledger = LoadLedger(); int num = 0; foreach (QuotaMilestoneConfig item in from m in Milestones.Values where m.Enable.Value && m.Quota <= completedQuota && m.GrantTokensPerPlayer.Value > 0 orderby m.Quota select m) { string text = QuotaRewardLedger.RewardId(item.Quota, "tokens"); if (QuotaRewardLedger.IsPaid(ledger, text) && QuotaProgressionApi.TryGrantTokens(new QuotaTokenGrantRequest(item.Quota, item.GrantTokensPerPlayer.Value, text, isReplay: true))) { num++; } } if (num > 0) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"Replayed {num} ledgered quota token grant(s) for connected players."); } } } internal static void OnQuotaCompleted() { ConfigEntry<bool> enabled = _enabled; if (enabled != null && enabled.Value) { NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && singleton.IsServer) { int num = (((Object)(object)TimeOfDay.Instance != (Object)null) ? Math.Max(0, TimeOfDay.Instance.timesFulfilledQuota) : 0); _currentPayload = BuildPayload(num); _hasCurrentPayload = true; SendPayload(_currentPayload); SendSoundtrack(BuildSoundtrackPlan()); SendMediaPlan(BuildMediaPlan(_currentPayload)); ApplyRewardsThroughQuota(num); QuotaProgressionSuitGate.OnUnlockStateMaybeChanged(num); } } } internal static QuotaTakeoverPayload GetCurrentPayload(string[] builtInDialogue) { int num = (((Object)(object)TimeOfDay.Instance != (Object)null) ? Math.Max(0, TimeOfDay.Instance.timesFulfilledQuota) : 0); QuotaTakeoverPayload result = ((_hasCurrentPayload && _currentPayload.Quota == num) ? _currentPayload : BuildPayload(num)); if (string.IsNullOrWhiteSpace(result.Dialogue) && builtInDialogue != null && builtInDialogue.Length != 0) { int num2 = (RandomizeDialogue ? GetRandomizedIndex(builtInDialogue.Length, num, "built-in") : (Math.Max(0, num - 1) % builtInDialogue.Length)); result = result.WithDialogue(builtInDialogue[num2], num2); } return result; } internal static void PrepareHostTakeoverPayload(string[] builtInDialogue) { NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && singleton.IsServer) { QuotaTakeoverPayload payload = (_currentPayload = GetCurrentPayload(builtInDialogue)); _hasCurrentPayload = true; SendPayload(payload); SendSoundtrack(BuildSoundtrackPlan()); SendMediaPlan(BuildMediaPlan(payload)); } } internal static bool IsContractUnlocked(string contractId, int completedQuotas) { return IsNamedUnlockSatisfied(contractId, completedQuotas, (QuotaMilestoneConfig milestone) => milestone.Contracts); } internal static bool IsShipUpgradePurchaseUnlocked(string upgradeId, int completedQuotas) { if ((Object)(object)NetworkManager.Singleton != (Object)null && !NetworkManager.Singleton.IsServer && _hasAuthoritativeUpgradeSnapshot) { return AuthoritativeUnlockedUpgrades.Contains(Normalize(upgradeId)); } return IsNamedUnlockSatisfied(upgradeId, completedQuotas, (QuotaMilestoneConfig milestone) => milestone.Upgrades); } internal static bool IsConstellationUnlocked(string name, int completedQuotas) { return IsNamedUnlockSatisfied(name, completedQuotas, (QuotaMilestoneConfig milestone) => milestone.Constellations); } internal static bool TryGetConstellationUnlockQuota(string name, out int quota) { quota = 0; ConfigEntry<bool> enabled = _enabled; if (enabled == null || !enabled.Value || string.IsNullOrWhiteSpace(name)) { return false; } if ((Object)(object)NetworkManager.Singleton != (Object)null && !NetworkManager.Singleton.IsServer && _hasAuthoritativeUpgradeSnapshot) { return AuthoritativeConstellationUnlockQuotas.TryGetValue(Normalize(name), out quota); } foreach (QuotaMilestoneConfig item in from m in Milestones.Values where m.Enable.Value orderby m.Quota select m) { if (item.Constellations.Any((string item) => Normalize(item) == Normalize(name))) { quota = item.Quota; return true; } } return false; } internal static bool IsSuitUnlocked(string name) { return IsRuleUnlocked(name, AuthoritativeSuitUnlockQuotas, (QuotaMilestoneConfig milestone) => milestone.Suits); } internal static bool IsStoreItemUnlocked(string name) { return IsRuleUnlocked(name, AuthoritativeStoreItemUnlockQuotas, (QuotaMilestoneConfig milestone) => milestone.StoreItems); } private static bool IsRuleUnlocked(string name, Dictionary<string, int> authoritative, Func<QuotaMilestoneConfig, IEnumerable<string>> selector) { ConfigEntry<bool> enabled = _enabled; if (enabled == null || !enabled.Value || string.IsNullOrWhiteSpace(name)) { return true; } if ((Object)(object)NetworkManager.Singleton != (Object)null && !NetworkManager.Singleton.IsServer && _hasAuthoritativeUpgradeSnapshot) { if (authoritative.TryGetValue(Normalize(name), out var value)) { return _authoritativeQuota >= value; } return true; } int completedQuotas = (((Object)(object)TimeOfDay.Instance != (Object)null) ? Math.Max(0, TimeOfDay.Instance.timesFulfilledQuota) : 0); return IsNamedUnlockSatisfied(name, completedQuotas, selector); } internal static QuotaMilestoneSummary GetMilestoneSummary(int quota) { if (!Milestones.TryGetValue(quota, out var value) || value == null) { return null; } return new QuotaMilestoneSummary { Quota = value.Quota, Enabled = value.Enable.Value, Contracts = value.Contracts.ToArray(), ShipUpgrades = value.Upgrades.ToArray(), Constellations = value.Constellations.ToArray(), Suits = value.Suits.ToArray(), StoreItems = value.StoreItems.ToArray(), TokensPerPlayer = value.GrantTokensPerPlayer.Value, GroupCredits = value.GrantGroupCredits.Value, ShipFuel = value.GrantShipFuel.Value }; } private static bool IsNamedUnlockSatisfied(string name, int completedQuotas, Func<QuotaMilestoneConfig, IEnumerable<string>> selector) { ConfigEntry<bool> enabled = _enabled; if (enabled == null || !enabled.Value || string.IsNullOrWhiteSpace(name)) { return true; } int? num = null; foreach (QuotaMilestoneConfig item in from m in Milestones.Values where m.Enable.Value orderby m.Quota select m) { if (selector(item).Any((string item) => Normalize(item) == Normalize(name))) { num = item.Quota; break; } } if (num.HasValue) { return completedQuotas >= num.Value; } return true; } private static QuotaTakeoverPayload BuildPayload(int quota) { QuotaMilestoneConfig value; QuotaMilestoneConfig quotaMilestoneConfig = ((Milestones.TryGetValue(quota, out value) && value.Enable.Value) ? value : null); List<QuotaMilestoneConfig> list = (from item in Milestones.Values where item.Enable.Value && !string.IsNullOrWhiteSpace(ReadDialogue(item)) orderby item.Quota select item).ToList(); if (RandomizeDialogue && list.Count > 0) { int randomizedIndex = GetRandomizedIndex(list.Count, quota, "configured"); quotaMilestoneConfig = list[randomizedIndex]; } string dialogue = ((quotaMilestoneConfig != null) ? ReadDialogue(quotaMilestoneConfig) : string.Empty); QuotaMilestoneConfig value2; string text = ((Milestones.TryGetValue(quota, out value2) && value2.Enable.Value) ? value2.MediaFile.Value : string.Empty); if (string.IsNullOrWhiteSpace(text)) { text = _defaultMediaFile?.Value ?? string.Empty; } YoutubeVideoResolver.Prefetch(text); string path; string mediaHash = ((!YoutubeVideoResolver.IsYoutubeUrl(text) && TryResolveMediaFile(text, out path)) ? ComputeSha256(path) : string.Empty); BuildAudioOverrideFields(out var alarmAudio, out var alarmAudioHash, out var mumbleAudio, out var mumbleAudioHashes); return new QuotaTakeoverPayload(quota, dialogue, text, mediaHash, quotaMilestoneConfig?.Quota ?? 0, alarmAudio, alarmAudioHash, mumbleAudio, mumbleAudioHashes); } private static void BuildAudioOverrideFields(out string alarmAudio, out string alarmAudioHash, out string mumbleAudio, out string mumbleAudioHashes) { alarmAudio = string.Empty; alarmAudioHash = string.Empty; string text = TruncateFixed((_alarmAudioFile?.Value ?? string.Empty).Trim(), 120); if (text.Length > 0 && TryResolveAudioFile(text, out var path)) { string text2 = ComputeSha256(path); if (text2.Length > 0) { alarmAudio = text; alarmAudioHash = text2; } } List<string> list = new List<string>(); List<string> list2 = new List<string>(); int num = 0; int num2 = 0; string[] array = SplitList(_mumbleAudioFiles?.Value); foreach (string value in array) { if (list.Count >= 12) { break; } string text3 = TruncateFixed(value, 120); if (!TryResolveAudioFile(text3, out var path2)) { continue; } string text4 = ComputeSha256(path2); if (text4.Length != 0) { int num3 = num + text3.Length + ((list.Count > 0) ? 1 : 0); int num4 = num2 + text4.Length + ((list2.Count > 0) ? 1 : 0); if (num3 > 1000 || num4 > 1000) { break; } list.Add(text3); list2.Add(text4); num = num3; num2 = num4; } } mumbleAudio = string.Join(",", list); mumbleAudioHashes = string.Join(",", list2); } private static string ReadDialogue(QuotaMilestoneConfig milestone) { if (milestone == null) { return string.Empty; } if (!string.IsNullOrWhiteSpace(milestone.DialogueFile.Value) && TryResolveFile(milestone.DialogueFile.Value, ".txt", out var path)) { try { if (new FileInfo(path).Length <= 65536) { return File.ReadAllText(path).Trim(); } ManualLogSource log = _log; if (log != null) { log.LogWarning((object)$"Quota {milestone.Quota} dialogue file exceeds 64 KB; using inline/built-in dialogue."); } } catch (Exception ex) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)$"Quota {milestone.Quota} dialogue file failed: {ex.Message}"); } } } return (milestone.Dialogue.Value ?? string.Empty).Replace("\\n", "\n").Trim(); } internal static bool TryResolveMediaFile(string configured, out string path) { if (YoutubeVideoResolver.IsYoutubeUrl(configured)) { return YoutubeVideoResolver.TryGetCachedVideo(configured, out path); } return TryResolveFile(configured, MediaExtensions, out path); } internal static bool TryResolveMediaFile(string configured, string expectedHash, out string path) { if (YoutubeVideoResolver.IsYoutubeUrl(configured)) { return YoutubeVideoResolver.TryGetCachedVideo(configured, out path); } return TryResolveVerifiedFile(configured, MediaExtensions, expectedHash, "media", out path); } internal static bool TryResolveAudioFile(string configured, out string path) { return TryResolveFile(configured, AudioExtensions, out path); } internal static bool TryResolveAudioFile(string configured, string expectedHash, out string path) { return TryResolveVerifiedFile(configured, AudioExtensions, expectedHash, "audio", out path); } private static bool TryResolveVerifiedFile(string configured, string[] extensions, string expectedHash, string kind, out string path) { if (!TryResolveFile(configured, extensions, out path)) { return false; } if (string.IsNullOrWhiteSpace(expectedHash)) { return true; } if (string.Equals(ComputeSha256(path), expectedHash, StringComparison.OrdinalIgnoreCase)) { return true; } ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Configured takeover " + kind + " hash mismatch for '" + Path.GetFileName(path) + "'; using bundled fallback.")); } path = null; return false; } private static bool TryResolveFile(string configured, string extension, out string path) { return TryResolveFile(configured, new string[1] { extension }, out path); } private static bool TryResolveFile(string configured, string[] extensions, out string path) { path = null; if (string.IsNullOrWhiteSpace(configured)) { return false; } string text = Path.GetFullPath(MediaDirectory).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; string fullPath = Path.GetFullPath(Path.Combine(text, configured.Trim())); if (!fullPath.StartsWith(text, StringComparison.OrdinalIgnoreCase) || !File.Exists(fullPath)) { return false; } if (!extensions.Contains<string>(Path.GetExtension(fullPath), StringComparer.OrdinalIgnoreCase)) { return false; } long num = (Path.GetExtension(fullPath).Equals(".mp4", StringComparison.OrdinalIgnoreCase) ? 536870912 : 33554432); if (new FileInfo(fullPath).Length > num) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Configured takeover media '" + Path.GetFileName(fullPath) + "' exceeds the size limit.")); } return false; } path = fullPath; return true; } internal static TakeoverAudioOverrideFile ResolveTakeoverAlarmAudio(QuotaTakeoverPayload payload) { if (string.IsNullOrWhiteSpace(payload.AlarmAudioFile)) { return default(TakeoverAudioOverrideFile); } if (TryResolveAudioFile(payload.AlarmAudioFile, payload.AlarmAudioHash, out var path)) { return new TakeoverAudioOverrideFile(path, payload.AlarmAudioHash); } ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Takeover alarm audio override '" + payload.AlarmAudioFile + "' is missing or does not match the host copy; using the built-in alarm.")); } return default(TakeoverAudioOverrideFile); } internal static List<TakeoverAudioOverrideFile> ResolveTakeoverMumbleAudio(QuotaTakeoverPayload payload) { List<TakeoverAudioOverrideFile> list = new List<TakeoverAudioOverrideFile>(); string[] array = SplitList(payload.MumbleAudioFiles); string[] array2 = SplitList(payload.MumbleAudioHashes); if (array.Length == 0) { return list; } int num = 0; for (int i = 0; i < array.Length; i++) { string text = ((i < array2.Length) ? array2[i] : string.Empty); if (TryResolveAudioFile(array[i], text, out var path)) { list.Add(new TakeoverAudioOverrideFile(path, text)); } else { num++; } } if (num > 0) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)$"{num} of {array.Length} takeover mumble audio overrides are missing or do not match the host copies; those slots use the built-in clips."); } } return list; } private static string[] SplitList(string value) { return (from item in (value ?? string.Empty).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select item.Trim() into item where item.Length > 0 select item).ToArray(); } internal static string[] SplitSemicolonList(string value) { return (from item in (value ?? string.Empty).Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries) select item.Trim() into item where item.Length > 0 select item).ToArray(); } internal static string[] SplitSemicolonListPreservingEmpty(string value) { string text = value ?? string.Empty; if (text.Length == 0) { return Array.Empty<string>(); } return (from item in text.Split(';') select item.Trim()).ToArray(); } private static void WarnOnCommaSeparatedMediaPool() { string text = _perMonitorMediaPool?.Value ?? string.Empty; if (text.IndexOf(';') < 0 && text.IndexOf(',') >= 0 && SplitList(text).Length >= 2) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"[QuotaProgression] PerMonitorMediaPool looks comma-separated; this key uses ';' so that a YouTube link containing a comma stays intact. The whole value is being read as one entry."); } } } private static void ApplyRewardsThroughQuota(int completedQuota) { HashSet<string> ledger = LoadLedger(); bool flag = false; foreach (QuotaMilestoneConfig milestone in from m in Milestones.Values where m.Enable.Value && m.Quota <= completedQuota orderby m.Quota select m) { flag |= ApplyReward(ledger, milestone, "credits", milestone.GrantGroupCredits.Value, providerAvailable: true, (float amount) => GrantCredits((int)amount)); flag |= ApplyBrokeredReward(ledger, milestone, "fuel", "ship_fuel", milestone.GrantShipFuel.Value); flag |= ApplyReward(ledger, milestone, "tokens", milestone.GrantTokensPerPlayer.Value, QuotaProgressionApi.IsTokenGrantCapabilityAvailable, (float _) => QuotaProgressionApi.TryGrantTokens(new QuotaTokenGrantRequest(milestone.Quota, milestone.GrantTokensPerPlayer.Value, QuotaRewardLedger.RewardId(milestone.Quota, "tokens")))); } if (flag) { SaveLedger(ledger); } } private static bool ApplyBrokeredReward(HashSet<string> ledger, QuotaMilestoneConfig milestone, string ledgerKind, string coreRewardKind, float amount) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) string rewardId = QuotaRewardLedger.RewardId(milestone.Quota, ledgerKind); bool wasDeferred = QuotaRewardLedger.IsDeferred(ledger, rewardId); QuotaRewardResult observed = (QuotaRewardResult)2; bool ledgerChanged; QuotaRewardOutcome quotaRewardOutcome = QuotaRewardLedger.Apply(ledger, rewardId, amount, providerAvailable: true, delegate(float offered) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) observed = QuotaRewardBroker.Offer(new QuotaRewardGrant(coreRewardKind, rewardId, milestone.Quota, (double)offered, wasDeferred)); return QuotaRewardOutcome.Consumes(observed); }, out ledgerChanged); if (quotaRewardOutcome == QuotaRewardOutcome.Deferred && !wasDeferred) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)($"Quota {milestone.Quota} {ledgerKind} reward is owed but nothing could be " + $"granted yet ({observed}); it stays unpaid and retries.")); } } else if (quotaRewardOutcome == QuotaRewardOutcome.Granted && wasDeferred) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)$"Deferred quota {milestone.Quota} {ledgerKind} reward granted."); } } return ledgerChanged; } private static bool ApplyReward(HashSet<string> ledger, QuotaMilestoneConfig milestone, string kind, float amount, bool providerAvailable, Func<float, bool> apply) { string rewardId = QuotaRewardLedger.RewardId(milestone.Quota, kind); bool flag = QuotaRewardLedger.IsDeferred(ledger, rewardId); bool ledgerChanged; QuotaRewardOutcome quotaRewardOutcome = QuotaRewardLedger.Apply(ledger, rewardId, amount, providerAvailable, apply, out ledgerChanged); if (quotaRewardOutcome == QuotaRewardOutcome.Deferred && !flag) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"Quota {milestone.Quota} {kind} reward is owed but nothing could be granted yet; it stays unpaid and retries."); } } else if (quotaRewardOutcome == QuotaRewardOutcome.Granted && flag) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)$"Deferred quota {milestone.Quota} {kind} reward granted."); } } return ledgerChanged; } private static bool GrantCredits(int amount) { Terminal val = Object.FindObjectOfType<Terminal>(); if ((Object)(object)val == (Object)null) { return false; } val.groupCredits = Math.Max(0, val.groupCredits + amount); val.SyncGroupCreditsServerRpc(val.groupCredits, val.numberOfItemsInDropship); return true; } private static HashSet<string> LoadLedger() { string text = CurrentSaveName(); if (_cachedLedger != null && string.Equals(_cachedLedgerSave, text, StringComparison.Ordinal)) { return _cachedLedger; } bool flag = false; try { _cachedLedger = new HashSet<string>((ES3.Load<string>("Y4NGZCompany.QuotaProgressionLedger.v1", text, string.Empty) ?? string.Empty).Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries), StringComparer.Ordinal); } catch (Exception ex) { flag = true; _cachedLedger = new HashSet<string>(StringComparer.Ordinal); ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Quota progression ledger load failed: " + ex.Message)); } } _cachedLedgerSave = text; if (!flag && QuotaRewardLedger.HealFalsePaidFuelRewards(_cachedLedger)) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)"Quota progression ledger healed: fuel milestones recorded as paid before the grant predicate was fixed are owed again."); } SaveLedger(_cachedLedger); } return _cachedLedger; } private static void SaveLedger(HashSet<string> ledger) { _cachedLedger = ledger; _cachedLedgerSave = CurrentSaveName(); try { ES3.Save<string>("Y4NGZCompany.QuotaProgressionLedger.v1", string.Join(";", ledger.OrderBy<string, string>((string item) => item, StringComparer.Ordinal)), _cachedLedgerSave); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Quota progression ledger save failed: " + ex.Message)); } } } private unsafe static void SendPayload(QuotaTakeoverPayload payload) { //IL_002f: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0152: 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_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null); if (val == null) { return; } FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(20480, (Allocator)2, -1); try { ((FastBufferWriter)(ref val2)).WriteValueSafe<int>(ref payload.Quota, default(ForPrimitives)); ((FastBufferWriter)(ref val2)).WriteValueSafe<int>(ref payload.SelectionId, default(ForPrimitives)); FixedString4096Bytes val3 = FixedString4096Bytes.op_Implicit(TruncateFixed(payload.Dialogue, 1000)); FixedString512Bytes val4 = FixedString512Bytes.op_Implicit(TruncateFixed(payload.MediaFile, YoutubeVideoResolver.IsYoutubeUrl(payload.MediaFile) ? 500 : 120)); FixedString128Bytes val5 = FixedString128Bytes.op_Implicit(payload.MediaHash); FixedString512Bytes val6 = FixedString512Bytes.op_Implicit(TruncateFixed(payload.AlarmAudioFile, 120)); FixedString128Bytes val7 = FixedString128Bytes.op_Implicit(payload.AlarmAudioHash); FixedString4096Bytes val8 = FixedString4096Bytes.op_Implicit(TruncateFixed(payload.MumbleAudioFiles, 1000)); FixedString4096Bytes val9 = FixedString4096Bytes.op_Implicit(TruncateFixed(payload.MumbleAudioHashes, 1000)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString4096Bytes>(ref val3, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString512Bytes>(ref val4, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString128Bytes>(ref val5, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString512Bytes>(ref val6, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString128Bytes>(ref val7, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString4096Bytes>(ref val8, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString4096Bytes>(ref val9, default(ForFixedStrings)); val.SendNamedMessageToAll("Y4NGZCompany.QuotaTakeoverPayload.v2", val2, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } private unsafe static void OnPayloadMessage(ulong sender, FastBufferReader reader) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if (sender == 0L) { int quota = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref quota, default(ForPrimitives)); int selectionId = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref selectionId, default(ForPrimitives)); FixedString4096Bytes val = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString4096Bytes>(ref val, default(ForFixedStrings)); FixedString512Bytes val2 = default(FixedString512Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString512Bytes>(ref val2, default(ForFixedStrings)); FixedString128Bytes val3 = default(FixedString128Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString128Bytes>(ref val3, default(ForFixedStrings)); FixedString512Bytes val4 = default(FixedString512Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString512Bytes>(ref val4, default(ForFixedStrings)); FixedString128Bytes val5 = default(FixedString128Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString128Bytes>(ref val5, default(ForFixedStrings)); FixedString4096Bytes val6 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString4096Bytes>(ref val6, default(ForFixedStrings)); FixedString4096Bytes val7 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString4096Bytes>(ref val7, default(ForFixedStrings)); _currentPayload = new QuotaTakeoverPayload(quota, ((object)(*(FixedString4096Bytes*)(&val))/*cast due to .constrained prefix*/).ToString(), ((object)(*(FixedString512Bytes*)(&val2))/*cast due to .constrained prefix*/).ToString(), ((object)(*(FixedString128Bytes*)(&val3))/*cast due to .constrained prefix*/).ToString(), selectionId, ((object)(*(FixedString512Bytes*)(&val4))/*cast due to .constrained prefix*/).ToString(), ((object)(*(FixedString128Bytes*)(&val5))/*cast due to .constrained prefix*/).ToString(), ((object)(*(FixedString4096Bytes*)(&val6))/*cast due to .constrained prefix*/).ToString(), ((object)(*(FixedString4096Bytes*)(&val7))/*cast due to .constrained prefix*/).ToString()); _hasCurrentPayload = true; YoutubeVideoResolver.Prefetch(_currentPayload.MediaFile); } } internal static TakeoverSoundtrackPlanWire GetSoundtrackPlan() { return _soundtrackPlan; } private static TakeoverSoundtrackPlanWire BuildSoundtrackPlan() { TakeoverSoundtrackMode soundtrackMode = SoundtrackMode; string text = (_soundtrackSource?.Value ?? string.Empty).Trim(); if (soundtrackMode == TakeoverSoundtrackMode.Off || text.Length == 0) { return TakeoverSoundtrackPlanWire.Off; } float soundtrackVolume = SoundtrackVolume; if (YoutubeVideoResolver.IsYoutubeUrl(text)) { YoutubeVideoResolver.Prefetch(text); string normalizedUrl; string value = (YoutubeVideoResolver.TryNormalizeYoutubeUrl(text, out normalizedUrl) ? normalizedUrl : text); return new TakeoverSoundtrackPlanWire(soundtrackMode, TruncateFixed(value, 500), string.Empty, soundtrackVolume); } string text2 = TruncateFixed(text, 120); if (TryResolveAudioFile(text2, out var path)) { string text3 = ComputeSha256(path); if (text3.Length > 0) { return new TakeoverSoundtrackPlanWire(soundtrackMode, text2, text3, soundtrackVolume); } } ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("[QuotaProgression] Takeover soundtrack '" + text2 + "' is missing or unreadable on the host; the takeover uses the built-in audio.")); } return TakeoverSoundtrackPlanWire.Off; } private unsafe static void SendSoundtrack(TakeoverSoundtrackPlanWire plan) { //IL_0038: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: 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_00c5: Unknown result type (might be due to invalid IL or missing references) _soundtrackPlan = plan; NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null); if (val == null) { return; } FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(2048, (Allocator)2, -1); try { int mode = (int)plan.Mode; ((FastBufferWriter)(ref val2)).WriteValueSafe<int>(ref mode, default(ForPrimitives)); FixedString512Bytes val3 = FixedString512Bytes.op_Implicit(TruncateFixed(plan.Source, YoutubeVideoResolver.IsYoutubeUrl(plan.Source) ? 500 : 120)); FixedString128Bytes val4 = FixedString128Bytes.op_Implicit(plan.Hash ?? string.Empty); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString512Bytes>(ref val3, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString128Bytes>(ref val4, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<float>(ref plan.Volume, default(ForPrimitives)); val.SendNamedMessageToAll("Y4NGZCompany.QuotaTakeoverSoundtrack.v1", val2, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } private unsafe static void OnSoundtrackMessage(ulong sender, FastBufferReader reader) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (sender == 0L) { int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); FixedString512Bytes val = default(FixedString512Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString512Bytes>(ref val, default(ForFixedStrings)); FixedString128Bytes val2 = default(FixedString128Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString128Bytes>(ref val2, default(ForFixedStrings)); float num2 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num2, default(ForPrimitives)); _soundtrackPlan = new TakeoverSoundtrackPlanWire(num switch { 2 => TakeoverSoundtrackMode.Mix, 1 => TakeoverSoundtrackMode.Replace, _ => TakeoverSoundtrackMode.Off, }, ((object)(*(FixedString512Bytes*)(&val))/*cast due to .constrained prefix*/).ToString(), ((object)(*(FixedString128Bytes*)(&val2))/*cast due to .constrained prefix*/).ToString(), Mathf.Clamp01(num2)); YoutubeVideoResolver.Prefetch(_soundtrackPlan.Source); } } internal static TakeoverMediaPlanWire GetMediaPlan() { return _mediaPlan; } private static TakeoverMediaPlanWire BuildMediaPlan(QuotaTakeoverPayload payload) { if (!RandomPerMonitorMedia) { return default(TakeoverMediaPlanWire); } int seed = Random.Range(int.MinValue, int.MaxValue); int maxConcurrentMediaPlayers = MaxConcurrentMediaPlayers; List<string> list = new List<string>(); List<string> list2 = new List<string>(); int num = 0; string[] array = PerMonitorMediaPool(); foreach (string text in array) { if (list.Count >= 6) { break; } string text2; string text3; if (YoutubeVideoResolver.IsYoutubeUrl(text)) { YoutubeVideoResolver.Prefetch(text); text2 = TruncateFixed(YoutubeVideoResolver.TryNormalizeYoutubeUrl(text, out var normalizedUrl) ? normalizedUrl : text, 500); text3 = string.Empty; } else { text2 = TruncateFixed(text, 120); if (!TryResolveMediaFile(text2, out var path)) { continue; } text3 = ComputeSha256(path); if (text3.Length == 0) { continue; } } int num2 = num + text2.Length + ((list.Count > 0) ? 1 : 0); if (num2 > 3000) { break; } list.Add(text2); list2.Add(text3); num = num2; } if (list.Count == 0) { string text4 = payload.MediaFile ?? string.Empty; if (text4.Length == 0) { return new TakeoverMediaPlanWire(seed, maxConcurrentMediaPlayers, null, null); } list.Add(TruncateFixed(text4, YoutubeVideoResolver.IsYoutubeUrl(text4) ? 500 : 120)); list2.Add(payload.MediaHash ?? string.Empty); } return new TakeoverMediaPlanWire(seed, maxConcurrentMediaPlayers, list.ToArray(), list2.ToArray()); } private unsafe static void SendMediaPlan(TakeoverMediaPlanWire plan) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: 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_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) _mediaPlan = plan; NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null); if (val == null) { return; } FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(20480, (Allocator)2, -1); try { ((FastBufferWriter)(ref val2)).WriteValueSafe<int>(ref plan.Seed, default(ForPrimitives)); ((FastBufferWriter)(ref val2)).WriteValueSafe<int>(ref plan.MaxPlayers, default(ForPrimitives)); FixedString4096Bytes val3 = FixedString4096Bytes.op_Implicit(TruncateFixed(string.Join(";", plan.Pool ?? Array.Empty<string>()), 3000)); FixedString4096Bytes val4 = FixedString4096Bytes.op_Implicit(TruncateFixed(string.Join(";", plan.Hashes ?? Array.Empty<string>()), 3000)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString4096Bytes>(ref val3, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString4096Bytes>(ref val4, default(ForFixedStrings)); val.SendNamedMessageToAll("Y4NGZCompany.QuotaTakeoverMediaPlan.v1", val2, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } private unsafe static void OnMediaPlanMessage(ulong sender, FastBufferReader reader) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (sender == 0L) { int seed = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref seed, default(ForPrimitives)); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); FixedString4096Bytes val = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString4096Bytes>(ref val, default(ForFixedStrings)); FixedString4096Bytes val2 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString4096Bytes>(ref val2, default(ForFixedStrings)); string[] array = SplitSemicolonList(((object)(*(FixedString4096Bytes*)(&val))/*cast due to .constrained prefix*/).ToString()); _mediaPlan = new TakeoverMediaPlanWire(seed, Mathf.Clamp(num, 1, 6), array, SplitSemicolonListPreservingEmpty(((object)(*(FixedString4096Bytes*)(&val2))/*cast due to .constrained prefix*/).ToString())); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { YoutubeVideoResolver.Prefetch(array2[i]); } } } private unsafe static void SendGateSnapshot() { //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null); if (val == null) { return; } NetworkManager singleton2 = NetworkManager.Singleton; if (singleton2 == null || !singleton2.IsServer) { return; } int quota = (((Object)(object)TimeOfDay.Instance != (Object)null) ? Math.Max(0, TimeOfDay.Instance.timesFulfilledQuota) : 0); string value = string.Join(",", (from id in QuotaUnlockCatalogs.GetStableIds("ship_upgrade") where IsNamedUnlockSatisfied(id, quota, (QuotaMilestoneConfig milestone) => milestone.Upgrades) select id).Select(Normalize)); string value2 = BuildRuleString((QuotaMilestoneConfig milestone) => milestone.Constellations); string value3 = BuildRuleString((QuotaMilestoneConfig milestone) => milestone.Suits); string value4 = BuildRuleString((QuotaMilestoneConfig milestone) => milestone.StoreItems); FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(17408, (Allocator)2, -1); try { ((FastBufferWriter)(ref val2)).WriteValueSafe<int>(ref quota, default(ForPrimitives)); FixedString4096Bytes val3 = FixedString4096Bytes.op_Implicit(TruncateFixed(value, 1000)); FixedString4096Bytes val4 = FixedString4096Bytes.op_Implicit(TruncateFixed(value2, 1000)); FixedString4096Bytes val5 = FixedString4096Bytes.op_Implicit(TruncateFixed(value3, 1000)); FixedString4096Bytes val6 = FixedString4096Bytes.op_Implicit(TruncateFixed(value4, 1000)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString4096Bytes>(ref val3, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString4096Bytes>(ref val4, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString4096Bytes>(ref val5, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe<FixedString4096Bytes>(ref val6, default(ForFixedStrings)); val.SendNamedMessageToAll("Y4NGZCompany.QuotaProgressionSnapshot.v2", val2, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } QuotaProgressionSuitGate.OnUnlockStateMaybeChanged(quota); } private static string BuildRuleString(Func<QuotaMilestoneConfig, IEnumerable<string>> selector) { return string.Join(";", from @group in (from m in Milestones.Values where m.Enable.Value orderby m.Quota select m).SelectMany((QuotaMilestoneConfig m) => from name in selector(m) select Normalize(name) + "=" + m.Quota).GroupBy<string, string>((string entry) => entry.Substring(0, entry.IndexOf('=')), StringComparer.OrdinalIgnoreCase) select @group.First()); } private static void ParseRuleString(string rules, Dictionary<string, int> target) { target.Clear(); string[] array = (rules ?? string.Empty).Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { int num = text.IndexOf('='); if (num > 0 && int.TryParse(text.Substring(num + 1), out var result)) { target[Normalize(text.Substring(0, num))] = result; } } } private unsafe static void OnGateSnapshotMessage(ulong sender, FastBufferReader reader) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) if (sender == 0L) { int val = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref val, default(ForPrimitives)); FixedString4096Bytes val2 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString4096Bytes>(ref val2, default(ForFixedStrings)); FixedString4096Bytes val3 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString4096Bytes>(ref val3, default(ForFixedStrings)); FixedString4096Bytes val4 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString4096Bytes>(ref val4, default(ForFixedStrings)); FixedString4096Bytes val5 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe<FixedString4096Bytes>(ref val5, default(ForFixedStrings)); AuthoritativeUnlockedUpgrades.Clear(); string[] array = ((object)(*(FixedString4096Bytes*)(&val2))/*cast due to .constrained prefix*/).ToString().Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string value in array) { AuthoritativeUnlockedUpgrades.Add(Normalize(value)); } ParseRuleString(((object)(*(FixedString4096Bytes*)(&val3))/*cast due to .constrained prefix*/).ToString(), AuthoritativeConstellationUnlockQuotas); ParseRuleString(((object)(*(FixedString4096Bytes*)(&val4))/*cast due to .constrained prefix*/).ToString(), AuthoritativeSuitUnlockQuotas); ParseRuleString(((object)(*(FixedString4096Bytes*)(&val5))/*cast due to .constrained prefix*/).ToString(), AuthoritativeStoreItemUnlockQuotas); _authoritativeQuota = Math.Max(0, val); _hasAuthoritativeUpgradeSnapshot = true; QuotaProgressionSuitGate.OnUnlockStateMaybeChanged(_authoritativeQuota); } } private static string Normalize(string value) { return new string((value ?? string.Empty).ToLowerInvariant().Where(char.IsLetterOrDigit).ToArray()); } private static string CurrentSaveName() { return GameNetworkManager.Instance?.currentSaveFileName ?? "unsaved"; } private static int StableHash(string value) { int num = 17; string text = value ?? string.Empty; foreach (char c in text) { num = num * 31 + c; } if (num != int.MinValue) { return num; } return 0; } private static void WriteEffectiveAudit() { try { string text = Y4NGZCompanyPaths.LocalDataFile("quota-progression-effective.tsv"); List<string> list = new List<string> { "quota\tenabled\tdialogue\tmedia\tcontracts\tupgrades\tconstellations\tsuits\tstore_items\ttokens_per_player\tgroup_credits\tship_fuel" }; foreach (QuotaMilestoneConfig item in Milestones.Values.OrderBy((QuotaMilestoneConfig m) => m.Quota)) { string value = ((!string.IsNullOrWhiteSpace(item.DialogueFile.Value)) ? ("file:" + item.DialogueFile.Value) : ((!string.IsNullOrWhiteSpace(item.Dialogue.Value)) ? "inline" : "built-in")); list.Add(string.Join("\t", item.Quota.ToString(), item.Enable.Value.ToString(), EscapeAudit(value), EscapeAudit(item.MediaFile.Value), EscapeAudit(string.Join(",", item.Contracts)), EscapeAudit(string.Join(",", item.Upgrades)), EscapeAudit(string.Join(",", item.Constellations)), EscapeAudit(string.Join(",", item.Suits)), EscapeAudit(string.Join(",", item.StoreItems)), item.GrantTokensPerPlayer.Value.ToString(), item.GrantGroupCredits.Value.ToString(), item.GrantShipFuel.Value.ToString(CultureInfo.InvariantCulture))); } list.Add(string.Empty); list.Add("shared_key\tvalue"); list.Add("DefaultMediaFile\t" + EscapeAudit(_defaultMediaFile?.Value)); list.Add("MumbleAudioFiles\t" + EscapeAudit(_mumbleAudioFiles?.Value)); list.Add("AlarmAudioFile\t" + EscapeAudit(_alarmAudioFile?.Value)); list.Add("SoundtrackSource\t" + EscapeAudit(_soundtrackSource?.Value)); list.Add("SoundtrackMode\t" + SoundtrackMode); list.Add("SoundtrackVolume\t" + SoundtrackVolume.ToString(CultureInfo.InvariantCulture)); list.Add("RandomPerMonitorMedia\t" + RandomPerMonitorMedia); list.Add("PerMonitorMediaPool\t" + EscapeAudit(_perMonitorMediaPool?.Value)); list.Add("MaxConcurrentMediaPlayers\t" + MaxConcurrentMediaPlayers); File.WriteAllLines(text, list); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"Quota progression effective audit written to '{text}' ({Milestones.Count} milestones)."); } } catch (Exception ex) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("Quota progression effective audit failed: " + ex.Message)); } } } private static string EscapeAudit(string value) { return (value ?? string.Empty).Replace("\t", "\\t").Replace("\r", "\\r").Replace("\n", "\\n"); } private static string TruncateFixed(string value, int maxChars) { value = value ?? string.Empty; if (value.Length > maxChars) { return value.Substring(0, maxChars); } return value; } private static int GetRandomizedIndex(int count, int quota, string salt) { if (count <= 1) { return 0; } ConfigEntry<bool> randomizeWithoutReplacement = _randomizeWithoutReplacement; if (randomizeWithoutReplacement == null || !randomizeWithoutReplacement.Value) { return Math.Abs(StableHash(CurrentSaveName() + ":" + salt + ":" + quota)) % count; } int[] array = Enumerable.Range(0, count).ToArray(); Random random = new Random(Math.Abs(StableHash(CurrentSaveName() + ":" + salt))); for (int num = array.Length - 1; num > 0; num--) { int num2 = random.Next(0, num + 1); int num3 = array[num]; array[num] = array[num2]; array[num2] = num3; } return array[Math.Max(0, quota - 1) % count]; } private static string ComputeSha256(string path) { try { using SHA256 sHA = SHA256.Create(); using FileStream inputStream = File.OpenRead(path); return BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", string.Empty).ToLowerInvariant(); } catch { return string.Empty; } } } internal static class QuotaProgressionSuitGate { private static readonly List<GameObject> HiddenSuits = new List<GameObject>(); private static readonly Vector3 ParkedLocalPosition = new Vector3(0f, -500f, 0f); private static int _lastAppliedQuota = -1; private static bool _repositioning; internal static string SuitUnlockableName(UnlockableSuit suit) { List<UnlockableItem> list = StartOfRound.Instance?.unlockablesList?.unlockables; int num = (((Object)(object)suit != (Object)null) ? suit.syncedSuitID.Value : (-1)); if (list == null || num < 0 || num >= list.Count) { return null; } return list[num]?.unlockableName; } private static void SetSuitHidden(UnlockableSuit suit, bool hidden) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) Renderer[] componentsInChildren = ((Component)suit).gameObject.GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = !hidden; } InteractTrigger[] componentsInChildren2 = ((Component)suit).gameObject.GetComponentsInChildren<InteractTrigger>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { ((Behaviour)componentsInChildren2[i]).enabled = !hidden; } Collider[] componentsInChildren3 = ((Component)suit).gameObject.GetComponentsInChildren<Collider>(true); for (int i = 0; i < componentsInChildren3.Length; i++) { componentsInChildren3[i].enabled = !hidden; } if (hidden) { ((Component)suit).transform.localPosition = ParkedLocalPosition; } } internal static void ApplyToRack() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) HiddenSuits.RemoveAll((GameObject item) => (Object)(object)item == (Object)null); UnlockableSuit[] array = Object.FindObjectsOfType<UnlockableSuit>(); foreach (UnlockableSuit val in array) { string text = SuitUnlockableName(val); if (!string.IsNullOrWhiteSpace(text) && !QuotaProgressionRegistry.IsSuitUnlocked(text)) { if (HiddenSuits.Contains(((Component)val).gameObject)) { ((Component)val).transform.localPosition = ParkedLocalPosition; continue; } SetSuitHidden(val, hidden: true); HiddenSuits.Add(((Component)val).gameObject); } } } internal static void OnUnlockStateMaybeChanged(int completedQuota) { if (completedQuota != _lastAppliedQuota) { _lastAppliedQuota = completedQuota; RefreshRack(); } } internal static void RefreshRack() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || _repositioning) { return; } foreach (GameObject hiddenSuit in HiddenSuits) { UnlockableSuit val = (((Object)(object)hiddenSuit != (Object)null) ? hiddenSuit.GetComponent<UnlockableSuit>() : null); if ((Object)(object)val != (Object)null) { SetSuitHidden(val, hidden: false); } } HiddenSuits.Clear(); _repositioning = true; try { instance.PositionSuitsOnRack(); } catch (Exception ex) { ModuleLog.Takeover.LogWarning((object)("Quota progression suit rack refresh failed: " + ex.Message)); } finally { _repositioning = false; } ApplyToRack(); } } internal static class QuotaProgressionUnlockPatches { private static TerminalNode _lockedNode; [HarmonyPatch(typeof(StartOfRound), "PositionSuitsOnRack")] [HarmonyPostfix] private static void OnPositionSuitsOnRack() { QuotaProgressionSuitGate.ApplyToRack(); } [HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")] [HarmonyPostfix] private static void OnParsePlayerSentence(Terminal __instance, ref TerminalNode __result) { if (TryGetLockedItemName(__instance, __result, out var itemName)) { __result = BuildLockedNode(itemName); } } [HarmonyPatch(typeof(Terminal), "LoadNewNodeIfAffordable")] [HarmonyPrefix] private static bool OnLoadNewNodeIfAffordable(Terminal __instance, TerminalNode node) { if (!TryGetLockedItemName(__instance, node, out var itemName)) { return true; } __instance.useCreditsCooldown = false; __instance.LoadNewNode(BuildLockedNode(itemName)); return false; } private static bool TryGetLockedItemName(Terminal terminal, TerminalNode node, out string itemName) { itemName = null; if (terminal?.buyableItemsList == null || (Object)(object)node == (Object)null) { return false; } if (node.buyItemIndex < 0 || node.buyItemIndex >= terminal.buyableItemsList.Length) { return false; } Item val = terminal.buyableItemsList[node.buyItemIndex]; if ((Object)(object)val == (Object)null || QuotaProgressionRegistry.IsStoreItemUnlocked(val.itemName)) { return false; } itemName = val.itemName; return true; } private static TerminalNode BuildLockedNode(string itemName) { if ((Object)(object)_lockedNode == (Object)null) { _lockedNode = ScriptableObject.CreateInstance<TerminalNode>(); ((Object)_lockedNode).name = "Y4NGZQuotaLockedItem"; _lockedNode.clearPreviousText = true; _lockedNode.buyItemIndex = -1; _lockedNode.shipUnlockableID = -1; _lockedNode.buyRerouteToMoon = -1; _lockedNode.creatureFileID = -1; _lockedNode.storyLogFileID = -1; _lockedNode.playSyncedClip = -1; } _lockedNode.displayText = "\n\n" + itemName + " is not currently available.\nThe Company will provision it after a future quota.\n\n"; return _lockedNode; } } internal enum QuotaRewardOutcome { NotConfigured, AlreadyPaid, Granted, Deferred } internal static