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 _tokenGrantHandler; public static bool IsTokenGrantCapabilityAvailable => _tokenGrantHandler != null; public static void RegisterTokenGrantHandler(Func handler) { _tokenGrantHandler = handler; } public static void UnregisterTokenGrantHandler(Func 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(); Hashes = hashes ?? Array.Empty(); } } 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 Enable; internal readonly ConfigEntry Dialogue; internal readonly ConfigEntry DialogueFile; internal readonly ConfigEntry MediaFile; internal readonly ConfigEntry UnlockContracts; internal readonly ConfigEntry UnlockShipUpgradePurchases; internal readonly ConfigEntry UnlockConstellations; internal readonly ConfigEntry UnlockSuits; internal readonly ConfigEntry UnlockStoreItems; internal readonly ConfigEntry GrantTokensPerPlayer; internal readonly ConfigEntry GrantGroupCredits; internal readonly ConfigEntry GrantShipFuel; internal IEnumerable Contracts => Split(UnlockContracts.Value); internal IEnumerable Upgrades => Split(UnlockShipUpgradePurchases.Value); internal IEnumerable Constellations => Split(UnlockConstellations.Value); internal IEnumerable Suits => Split(UnlockSuits.Value); internal IEnumerable 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(text, "Enable", true, "Enable this quota milestone."); Dialogue = config.Bind(text, "Dialogue", string.Empty, "Inline takeover dialogue. Use \\n for line breaks; blank uses built-in dialogue."); DialogueFile = config.Bind(text, "DialogueFile", string.Empty, "Optional UTF-8 .txt file under BepInEx/config/Y4NGZCompany/MonitorTakeovers."); MediaFile = config.Bind(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(text, "UnlockContracts", string.Empty, "Comma-separated stable contract IDs that begin rolling at this quota."); UnlockShipUpgradePurchases = config.Bind(text, "UnlockShipUpgradePurchases", string.Empty, "Comma-separated stable ship-upgrade IDs unlocked for purchase at this quota."); UnlockConstellations = config.Bind(text, "UnlockConstellations", string.Empty, "Comma-separated constellation names unlocked at this quota."); UnlockSuits = config.Bind(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(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(text, "GrantTokensPerPlayer", 3, new ConfigDescription("Tokens granted once per eligible player through Y4NGZUpgrades.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 1000), Array.Empty())); GrantGroupCredits = config.Bind(text, "GrantGroupCredits", 0, new ConfigDescription("Group credits granted once when this quota completes.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100000), Array.Empty())); GrantShipFuel = config.Bind(text, "GrantShipFuel", 0f, new ConfigDescription("Ship fuel granted once when this quota completes.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1000f), Array.Empty())); } private static IEnumerable 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 Contracts; internal IReadOnlyList ShipUpgrades; internal IReadOnlyList Constellations; internal IReadOnlyList Suits; internal IReadOnlyList StoreItems; internal int TokensPerPlayer; internal int GroupCredits; internal float ShipFuel; } [CompilerGenerated] private static class <>O { public static Action <0>__OnQuotaRolloverFinalized; public static Action <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 <6>__OnClientConnected; public static Func <7>__Normalize; public static Func <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 Milestones = new Dictionary(); 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 _enabled; private static ConfigEntry _retryPending; private static ConfigEntry _randomizeDialogue; private static ConfigEntry _randomizeWithoutReplacement; private static ConfigEntry _defaultMediaFile; private static ConfigEntry _mumbleAudioFiles; private static ConfigEntry _alarmAudioFile; private static ConfigEntry _soundtrackSource; private static ConfigEntry _soundtrackMode; private static ConfigEntry _soundtrackVolume; private static ConfigEntry _loopMedia; private static ConfigEntry _enableMediaAudio; private static ConfigEntry _useMediaLengthAsDuration; private static ConfigEntry _randomPerMonitorMedia; private static ConfigEntry _perMonitorMediaPool; private static ConfigEntry _maxConcurrentMediaPlayers; private static ConfigEntry _enableUnlockAnnouncement; private static ConfigEntry _unlockAnnouncementSeconds; private static ConfigEntry _announceOnBothMonitors; private static ConfigEntry _rewardsCeremonyEnabled; private static ConfigEntry _ceremonyMirrorBothMonitors; private static ConfigEntry _ceremonyTypewriterSecondsPerChar; private static ConfigEntry _ceremonyLineConfirmSting; private static ConfigEntry _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 AuthoritativeUnlockedUpgrades = new HashSet(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary AuthoritativeConstellationUnlockQuotas = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary AuthoritativeSuitUnlockQuotas = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary AuthoritativeStoreItemUnlockQuotas = new Dictionary(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 _cachedLedger; private static Action _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("General", "EnableQuotaProgression", true, "Enable quota-based unlock gates, rewards, and takeover presentation."); _retryPending = config.Bind("General", "RetryPendingOptionalRewards", true, "Retry optional rewards such as tokens when their provider becomes available later."); _randomizeDialogue = config.Bind("Presentation", "RandomizeDialogue", false, "Choose configured dialogue from any enabled quota instead of attaching dialogue to its quota."); _randomizeWithoutReplacement = config.Bind("Presentation", "RandomizeWithoutReplacement", true, "When randomizing, use every available passage once before the deterministic pool repeats."); _defaultMediaFile = config.Bind("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("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("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("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("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("Audio", "SoundtrackVolume", 0.8f, new ConfigDescription("Soundtrack volume (0 = silent, 1 = full). Independent of Mumble Volume.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); _loopMedia = config.Bind("Media", "LoopMedia", true, "Loop configured MP4 media for the duration of the takeover."); _enableMediaAudio = config.Bind("Media", "EnableMediaAudio", false, "Play the configured MP4 audio track. Disabled by default so it does not compete with takeover speech."); _useMediaLengthAsDuration = config.Bind("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("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("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("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(1, 6), Array.Empty())); _enableUnlockAnnouncement = config.Bind("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("Rewards Ceremony", "UnlockAnnouncementSeconds", 4.5f, new ConfigDescription("Seconds the unlock announcement stays on the monitor.", (AcceptableValueBase)(object)new AcceptableValueRange(2f, 15f), Array.Empty())); _announceOnBothMonitors = config.Bind("Rewards Ceremony", "AnnounceOnBothMonitors", false, "Also show the unlock announcement on the right large ship monitor."); _rewardsCeremonyEnabled = config.Bind("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("Rewards Ceremony", "CeremonyMirrorBothMonitors", true, "During the rewards ceremony, mirror the announcement onto the right large ship monitor as well."); _ceremonyTypewriterSecondsPerChar = config.Bind("Rewards Ceremony", "CeremonyTypewriterSecondsPerChar", 0.03f, new ConfigDescription("Seconds each typed character takes during the rewards ceremony.", (AcceptableValueBase)(object)new AcceptableValueRange(0.005f, 0.2f), Array.Empty())); _ceremonyLineConfirmSting = config.Bind("Rewards Ceremony", "CeremonyLineConfirmSting", true, "Play a confirm sting as each reward line finishes typing."); _ceremonyRestoreStatic = config.Bind("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 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 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 enabled = _enabled; if (enabled != null && enabled.Value) { _tokenReplayAttemptsRemaining = 3; _nextTokenReplayAt = Time.realtimeSinceStartup + 6f; } } } private static void ReplayLedgeredTokenGrants() { ConfigEntry 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 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 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 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 authoritative, Func> selector) { ConfigEntry 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> selector) { ConfigEntry 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 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 list = new List(); List list2 = new List(); 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(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 ResolveTakeoverMumbleAudio(QuotaTakeoverPayload payload) { List list = new List(); 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(); } 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 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 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 ledger, QuotaMilestoneConfig milestone, string kind, float amount, bool providerAvailable, Func 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(); 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 LoadLedger() { string text = CurrentSaveName(); if (_cachedLedger != null && string.Equals(_cachedLedgerSave, text, StringComparison.Ordinal)) { return _cachedLedger; } bool flag = false; try { _cachedLedger = new HashSet((ES3.Load("Y4NGZCompany.QuotaProgressionLedger.v1", text, string.Empty) ?? string.Empty).Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries), StringComparer.Ordinal); } catch (Exception ex) { flag = true; _cachedLedger = new HashSet(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 ledger) { _cachedLedger = ledger; _cachedLedgerSave = CurrentSaveName(); try { ES3.Save("Y4NGZCompany.QuotaProgressionLedger.v1", string.Join(";", ledger.OrderBy((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(ref payload.Quota, default(ForPrimitives)); ((FastBufferWriter)(ref val2)).WriteValueSafe(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(ref val3, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref val4, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref val5, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref val6, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref val7, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref val8, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(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(ref quota, default(ForPrimitives)); int selectionId = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref selectionId, default(ForPrimitives)); FixedString4096Bytes val = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val, default(ForFixedStrings)); FixedString512Bytes val2 = default(FixedString512Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val2, default(ForFixedStrings)); FixedString128Bytes val3 = default(FixedString128Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val3, default(ForFixedStrings)); FixedString512Bytes val4 = default(FixedString512Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val4, default(ForFixedStrings)); FixedString128Bytes val5 = default(FixedString128Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val5, default(ForFixedStrings)); FixedString4096Bytes val6 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val6, default(ForFixedStrings)); FixedString4096Bytes val7 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(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(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(ref val3, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref val4, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(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(ref num, default(ForPrimitives)); FixedString512Bytes val = default(FixedString512Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val, default(ForFixedStrings)); FixedString128Bytes val2 = default(FixedString128Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val2, default(ForFixedStrings)); float num2 = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(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 list = new List(); List list2 = new List(); 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(ref plan.Seed, default(ForPrimitives)); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref plan.MaxPlayers, default(ForPrimitives)); FixedString4096Bytes val3 = FixedString4096Bytes.op_Implicit(TruncateFixed(string.Join(";", plan.Pool ?? Array.Empty()), 3000)); FixedString4096Bytes val4 = FixedString4096Bytes.op_Implicit(TruncateFixed(string.Join(";", plan.Hashes ?? Array.Empty()), 3000)); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref val3, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(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(ref seed, default(ForPrimitives)); int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); FixedString4096Bytes val = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val, default(ForFixedStrings)); FixedString4096Bytes val2 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(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(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(ref val3, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref val4, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref val5, default(ForFixedStrings)); ((FastBufferWriter)(ref val2)).WriteValueSafe(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> 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 entry) => entry.Substring(0, entry.IndexOf('=')), StringComparer.OrdinalIgnoreCase) select @group.First()); } private static void ParseRuleString(string rules, Dictionary 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(ref val, default(ForPrimitives)); FixedString4096Bytes val2 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val2, default(ForFixedStrings)); FixedString4096Bytes val3 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val3, default(ForFixedStrings)); FixedString4096Bytes val4 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val4, default(ForFixedStrings)); FixedString4096Bytes val5 = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(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 list = new List { "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 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 HiddenSuits = new List(); 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 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(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = !hidden; } InteractTrigger[] componentsInChildren2 = ((Component)suit).gameObject.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { ((Behaviour)componentsInChildren2[i]).enabled = !hidden; } Collider[] componentsInChildren3 = ((Component)suit).gameObject.GetComponentsInChildren(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(); 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() : 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(); ((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 class QuotaRewardLedger { internal const string DeferredSuffix = ":deferred"; internal const string FuelHealMarker = "schema:fuel-heal.v1"; private const string FuelKind = "fuel"; internal static string RewardId(int quota, string kind) { return "q" + quota + ":" + (kind ?? string.Empty); } internal static string DeferredId(string rewardId) { return (rewardId ?? string.Empty) + ":deferred"; } internal static bool IsPaid(ICollection ledger, string rewardId) { if (ledger != null && !string.IsNullOrEmpty(rewardId)) { return ledger.Contains(rewardId); } return false; } internal static bool IsDeferred(ICollection ledger, string rewardId) { if (!IsPaid(ledger, rewardId) && ledger != null && !string.IsNullOrEmpty(rewardId)) { return ledger.Contains(DeferredId(rewardId)); } return false; } internal static QuotaRewardOutcome Apply(ICollection ledger, string rewardId, float amount, bool providerAvailable, Func grant) { bool ledgerChanged; return Apply(ledger, rewardId, amount, providerAvailable, grant, out ledgerChanged); } internal static QuotaRewardOutcome Apply(ICollection ledger, string rewardId, float amount, bool providerAvailable, Func grant, out bool ledgerChanged) { ledgerChanged = false; if (ledger == null || string.IsNullOrEmpty(rewardId) || amount <= 0f) { return QuotaRewardOutcome.NotConfigured; } if (ledger.Contains(rewardId)) { return QuotaRewardOutcome.AlreadyPaid; } if (!providerAvailable || grant == null || !grant(amount)) { string item = DeferredId(rewardId); if (!ledger.Contains(item)) { ledger.Add(item); ledgerChanged = true; } return QuotaRewardOutcome.Deferred; } if (ledger.Remove(DeferredId(rewardId))) { ledgerChanged = true; } if (!ledger.Contains(rewardId)) { ledger.Add(rewardId); ledgerChanged = true; } return QuotaRewardOutcome.Granted; } internal static bool HasDeferredRewards(IEnumerable ledger) { return ledger?.Any((string entry) => entry?.EndsWith(":deferred", StringComparison.Ordinal) ?? false) ?? false; } internal static bool ShouldRetryRewards(bool retryOptionalRewardsEnabled, IEnumerable ledger) { if (!retryOptionalRewardsEnabled) { return HasDeferredRewards(ledger); } return true; } internal static bool HealFalsePaidFuelRewards(ICollection ledger) { if (ledger == null || ledger.Contains("schema:fuel-heal.v1")) { return false; } foreach (string item2 in ledger.Where(IsFuelRewardId).ToList()) { ledger.Remove(item2); string item = DeferredId(item2); if (!ledger.Contains(item)) { ledger.Add(item); } } ledger.Add("schema:fuel-heal.v1"); return true; } private static bool IsFuelRewardId(string entry) { if (string.IsNullOrEmpty(entry) || entry[0] != 'q') { return false; } int num = entry.IndexOf(':'); if (num < 2) { return false; } if (!string.Equals(entry.Substring(num + 1), "fuel", StringComparison.Ordinal)) { return false; } for (int i = 1; i < num; i++) { if (!char.IsDigit(entry[i])) { return false; } } return true; } } internal sealed class YoutubeVideoStreams { internal string VideoUrl; } internal static class YoutubeVideoResolver { private sealed class CachedResolution { internal YoutubeVideoStreams Streams; internal DateTime ExpiresUtc; } private sealed class ProcessResult { internal int ExitCode; internal string StandardOutput; internal string StandardError; internal bool TimedOut; } private sealed class CropRegion { internal int SourceWidth; internal int SourceHeight; internal int Width; internal int Height; internal int X; internal int Y; } private const string CacheFormatVersion = "screen-fill-v1"; private const int MaximumSourceLength = 4096; private const int MaximumLoggedErrorLength = 400; private const int MinimumBinaryLength = 102400; private const int MinimumVideoLength = 65536; private const long MaximumVideoBytes = 268435456L; private const long FfmpegArchiveLength = 54681842L; private const long FfmpegExecutableLength = 134163456L; private const string UnityCompatibleFormat = "bestvideo[ext=mp4][height<=480][vcodec^=avc1]+bestaudio[ext=m4a]/best[ext=mp4][height<=480][vcodec^=avc1][acodec^=mp4a]"; private const string FfmpegArchiveUrl = "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v6.1/ffmpeg-6.1-win-64.zip"; private const string FfmpegArchiveSha256 = "B0FB4BCEF9D4B5F7A77D2E4854F80D4CE3E43809BC29FD1F97CAA1B467F96993"; private const string FfmpegExecutableSha256 = "BA242553F0FF60AD788069D5D376C1B4F7A2F3A3566416E0ED950CA7920DA5FA"; private static readonly Regex CropRectanglePattern = new Regex("crop=(?\\d+):(?\\d+):(?\\d+):(?\\d+)", RegexOptions.Compiled | RegexOptions.CultureInvariant); private static readonly Regex FrameSizePattern = new Regex("\\bs:(?\\d{2,5})x(?\\d{2,5})\\b", RegexOptions.Compiled | RegexOptions.CultureInvariant); private static readonly TimeSpan BinaryRefreshInterval = TimeSpan.FromHours(24.0); private static readonly TimeSpan FailedRefreshRetryInterval = TimeSpan.FromHours(1.0); private static readonly TimeSpan ResolvedUrlCacheLifetime = TimeSpan.FromMinutes(10.0); private static readonly TimeSpan ProcessTimeout = TimeSpan.FromMinutes(3.0); private static readonly object Gate = new object(); private static readonly SemaphoreSlim BinaryGate = new SemaphoreSlim(1, 1); private static readonly Dictionary CachedUrls = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary> InFlightResolutions = new Dictionary>(StringComparer.Ordinal); private static readonly HttpClient DownloadClient = CreateDownloadClient(); private static DateTime _nextBinaryRefreshUtc; private static string _validatedFfmpegPath; internal static bool IsYoutubeUrl(string value) { string normalizedUrl; return TryNormalizeYoutubeUrl(value, out normalizedUrl); } internal static bool TryGetCachedVideo(string value, out string path) { path = null; if (!TryNormalizeYoutubeUrl(value, out var normalizedUrl)) { return false; } string text = ComputeTextSha256("screen-fill-v1\n" + normalizedUrl); string text2 = Path.Combine(Path.Combine(GetToolCacheRoot(), "videos"), text + ".mp4"); if (!IsValidCachedVideo(text2)) { return false; } path = text2; return true; } internal static void Prefetch(string value) { if (IsYoutubeUrl(value) && !TryGetCachedVideo(value, out var _)) { ResolveAsync(value); } } internal static Task ResolveAsync(string value) { if (!TryNormalizeYoutubeUrl(value, out var normalizedUrl)) { return Task.FromResult(null); } lock (Gate) { DateTime utcNow = DateTime.UtcNow; if (CachedUrls.TryGetValue(normalizedUrl, out var value2) && value2.ExpiresUtc > utcNow) { return Task.FromResult(value2.Streams); } CachedUrls.Remove(normalizedUrl); if (InFlightResolutions.TryGetValue(normalizedUrl, out var value3)) { return value3; } TaskCompletionSource taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); InFlightResolutions[normalizedUrl] = taskCompletionSource.Task; ResolveAndPublishAsync(normalizedUrl, taskCompletionSource); return taskCompletionSource.Task; } } internal static bool TryNormalizeYoutubeUrl(string value, out string normalizedUrl) { normalizedUrl = null; if (string.IsNullOrWhiteSpace(value)) { return false; } string text = value.Trim(); if (text.Length > 4096 || !Uri.TryCreate(text, UriKind.Absolute, out Uri result) || (result.Scheme != Uri.UriSchemeHttp && result.Scheme != Uri.UriSchemeHttps) || !result.IsDefaultPort || !string.IsNullOrEmpty(result.UserInfo)) { return false; } string text2 = result.IdnHost.TrimEnd('.').ToLowerInvariant(); bool flag = text2 == "youtu.be"; bool flag2 = text2 == "youtube.com" || text2.EndsWith(".youtube.com", StringComparison.Ordinal) || text2 == "youtube-nocookie.com" || text2.EndsWith(".youtube-nocookie.com", StringComparison.Ordinal); if ((!flag && !flag2) || !HasRecognizedVideoPath(result, flag)) { return false; } UriBuilder uriBuilder = new UriBuilder(result) { Scheme = Uri.UriSchemeHttps, Port = -1, Fragment = string.Empty }; normalizedUrl = uriBuilder.Uri.AbsoluteUri; return normalizedUrl.Length <= 4096; } private static async Task ResolveAndPublishAsync(string normalizedUrl, TaskCompletionSource completion) { YoutubeVideoStreams resolvedStreams = null; try { resolvedStreams = await ResolveCoreAsync(normalizedUrl).ConfigureAwait(continueOnCapturedContext: false); } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[MonitorTakeover] YouTube resolution failed: " + GetSafeError(ex.Message))); } } lock (Gate) { InFlightResolutions.Remove(normalizedUrl); if (resolvedStreams != null) { CachedUrls[normalizedUrl] = new CachedResolution { Streams = resolvedStreams, ExpiresUtc = DateTime.UtcNow + ResolvedUrlCacheLifetime }; } } completion.TrySetResult(resolvedStreams); } private static async Task ResolveCoreAsync(string normalizedUrl) { string videoDirectory = Path.Combine(GetToolCacheRoot(), "videos"); Directory.CreateDirectory(videoDirectory); string cacheKey = ComputeTextSha256("screen-fill-v1\n" + normalizedUrl); string cachedVideoPath = Path.Combine(videoDirectory, cacheKey + ".mp4"); if (IsValidCachedVideo(cachedVideoPath)) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogInfo((object)("[MonitorTakeover] Using cached YouTube video " + GetSafeSourceLabel(normalizedUrl) + ".")); } return new YoutubeVideoStreams { VideoUrl = cachedVideoPath }; } TryDeleteFile(cachedVideoPath); DeleteWorkingVideoFiles(videoDirectory, cacheKey); string binaryPath = await EnsureYtDlpAsync().ConfigureAwait(continueOnCapturedContext: false); string ffmpegPath = await EnsureFfmpegAsync().ConfigureAwait(continueOnCapturedContext: false); string workingVideoPath = Path.Combine(videoDirectory, cacheKey + ".working.mp4"); string normalizedVideoPath = Path.Combine(videoDirectory, cacheKey + ".working.fill.mp4"); ManualLogSource log2 = TakeoverBootstrap.Log; if (log2 != null) { log2.LogInfo((object)("[MonitorTakeover] Downloading and caching YouTube video " + GetSafeSourceLabel(normalizedUrl) + ".")); } try { ProcessResult processResult = await RunYtDlpAsync(binaryPath, ffmpegPath, workingVideoPath, normalizedUrl).ConfigureAwait(continueOnCapturedContext: false); if (processResult.TimedOut) { ManualLogSource log3 = TakeoverBootstrap.Log; if (log3 != null) { log3.LogWarning((object)"[MonitorTakeover] yt-dlp timed out while caching the configured video."); } return null; } if (processResult.ExitCode != 0) { ManualLogSource log4 = TakeoverBootstrap.Log; if (log4 != null) { log4.LogWarning((object)$"[MonitorTakeover] yt-dlp exited with code {processResult.ExitCode}: {GetSafeError(processResult.StandardError)}"); } return null; } if (!IsValidCachedVideo(workingVideoPath)) { ManualLogSource log5 = TakeoverBootstrap.Log; if (log5 != null) { log5.LogWarning((object)"[MonitorTakeover] yt-dlp completed, but the merged MP4 was missing or invalid."); } return null; } string completedVideoPath = workingVideoPath; CropRegion cropRegion = await DetectEncodedBordersAsync(ffmpegPath, workingVideoPath).ConfigureAwait(continueOnCapturedContext: false); if (cropRegion != null) { ManualLogSource log6 = TakeoverBootstrap.Log; if (log6 != null) { log6.LogInfo((object)($"[MonitorTakeover] Removing encoded video borders: crop {cropRegion.Width}x{cropRegion.Height} " + $"at {cropRegion.X},{cropRegion.Y}, then fill {cropRegion.SourceWidth}x{cropRegion.SourceHeight}.")); } ProcessResult processResult2 = await RunProcessAsync(ffmpegPath, BuildFillFrameArguments(workingVideoPath, normalizedVideoPath, cropRegion)).ConfigureAwait(continueOnCapturedContext: false); if (processResult2.TimedOut) { ManualLogSource log7 = TakeoverBootstrap.Log; if (log7 != null) { log7.LogWarning((object)"[MonitorTakeover] FFmpeg timed out while removing encoded video borders; using the original frame."); } } else if (processResult2.ExitCode != 0 || !IsValidCachedVideo(normalizedVideoPath)) { ManualLogSource log8 = TakeoverBootstrap.Log; if (log8 != null) { log8.LogWarning((object)("[MonitorTakeover] FFmpeg could not remove encoded video borders; using the original frame: " + GetSafeError(processResult2.StandardError))); } } else { completedVideoPath = normalizedVideoPath; } } File.Move(completedVideoPath, cachedVideoPath); ManualLogSource log9 = TakeoverBootstrap.Log; if (log9 != null) { log9.LogInfo((object)("[MonitorTakeover] YouTube video cached as a screen-filling local H.264/AAC MP4 " + $"({new FileInfo(cachedVideoPath).Length / 1024 / 1024} MiB).")); } return new YoutubeVideoStreams { VideoUrl = cachedVideoPath }; } finally { DeleteWorkingVideoFiles(videoDirectory, cacheKey); } } private static string GetToolCacheRoot() { string text = Path.Combine((string.IsNullOrWhiteSpace(Paths.CachePath) ? Path.GetDirectoryName(typeof(YoutubeVideoResolver).Assembly.Location) : Paths.CachePath) ?? string.Empty, "Y4NGZMonitorTakeover"); Directory.CreateDirectory(text); return text; } private static async Task EnsureYtDlpAsync() { await BinaryGate.WaitAsync().ConfigureAwait(continueOnCapturedContext: false); try { string toolCacheRoot = GetToolCacheRoot(); string ytDlpBinaryName = GetYtDlpBinaryName(); string binaryPath = Path.Combine(toolCacheRoot, ytDlpBinaryName); DateTime now = DateTime.UtcNow; bool exists = File.Exists(binaryPath) && new FileInfo(binaryPath).Length >= 102400; if ((exists && now - File.GetLastWriteTimeUtc(binaryPath) <= BinaryRefreshInterval) || (exists && now < _nextBinaryRefreshUtc)) { EnsureExecutablePermission(binaryPath); return binaryPath; } ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogInfo((object)(exists ? "[MonitorTakeover] Refreshing the cached yt-dlp executable." : "[MonitorTakeover] Downloading yt-dlp for first YouTube playback.")); } try { string requestUri = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/" + ytDlpBinaryName; byte[] array = await DownloadClient.GetByteArrayAsync(requestUri).ConfigureAwait(continueOnCapturedContext: false); if (array == null || array.Length < 102400) { throw new InvalidDataException("The yt-dlp download was unexpectedly small."); } string text = binaryPath + ".download"; try { File.WriteAllBytes(text, array); File.Copy(text, binaryPath, overwrite: true); } finally { if (File.Exists(text)) { File.Delete(text); } } EnsureExecutablePermission(binaryPath); _nextBinaryRefreshUtc = now + BinaryRefreshInterval; return binaryPath; } catch (Exception ex) { if (!exists) { throw new IOException("yt-dlp could not be downloaded and no cached copy is available.", ex); } _nextBinaryRefreshUtc = now + FailedRefreshRetryInterval; ManualLogSource log2 = TakeoverBootstrap.Log; if (log2 != null) { log2.LogWarning((object)("[MonitorTakeover] Could not refresh yt-dlp; using the cached executable: " + GetSafeError(ex.Message))); } EnsureExecutablePermission(binaryPath); return binaryPath; } } finally { BinaryGate.Release(); } } private static async Task EnsureFfmpegAsync() { string text = FindExecutableOnPath(IsWindowsPlatform() ? "ffmpeg.exe" : "ffmpeg"); if (!string.IsNullOrEmpty(text)) { return text; } await BinaryGate.WaitAsync().ConfigureAwait(continueOnCapturedContext: false); try { if (!string.IsNullOrEmpty(_validatedFfmpegPath) && File.Exists(_validatedFfmpegPath)) { return _validatedFfmpegPath; } if (!IsWindowsPlatform()) { throw new PlatformNotSupportedException("FFmpeg was not found on PATH. Install FFmpeg so yt-dlp can merge YouTube video and audio streams."); } string toolCacheRoot = GetToolCacheRoot(); string text2 = Path.Combine(toolCacheRoot, "ffmpeg-6.1"); Directory.CreateDirectory(text2); string ffmpegPath = Path.Combine(text2, "ffmpeg.exe"); if (IsVerifiedFile(ffmpegPath, 134163456L, "BA242553F0FF60AD788069D5D376C1B4F7A2F3A3566416E0ED950CA7920DA5FA")) { _validatedFfmpegPath = ffmpegPath; return ffmpegPath; } TryDeleteFile(ffmpegPath); string archivePath = Path.Combine(toolCacheRoot, "ffmpeg-6.1-win-64.zip.download"); string executableDownloadPath = ffmpegPath + ".download"; TryDeleteFile(archivePath); TryDeleteFile(executableDownloadPath); ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogInfo((object)"[MonitorTakeover] FFmpeg was not found on PATH; downloading the verified 52 MiB playback helper once."); } try { await DownloadFileAsync("https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v6.1/ffmpeg-6.1-win-64.zip", archivePath).ConfigureAwait(continueOnCapturedContext: false); if (!IsVerifiedFile(archivePath, 54681842L, "B0FB4BCEF9D4B5F7A77D2E4854F80D4CE3E43809BC29FD1F97CAA1B467F96993")) { throw new InvalidDataException("The downloaded FFmpeg archive failed integrity verification."); } using (FileStream archiveStream = new FileStream(archivePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { using ZipArchive archive = new ZipArchive(archiveStream, ZipArchiveMode.Read, leaveOpen: false); ZipArchiveEntry zipArchiveEntry = null; for (int i = 0; i < archive.Entries.Count; i++) { ZipArchiveEntry zipArchiveEntry2 = archive.Entries[i]; if (string.Equals(zipArchiveEntry2.FullName, "ffmpeg.exe", StringComparison.Ordinal) && zipArchiveEntry2.Length == 134163456) { zipArchiveEntry = zipArchiveEntry2; break; } } if (zipArchiveEntry == null) { throw new InvalidDataException("The verified FFmpeg archive did not contain the expected executable."); } using Stream input = zipArchiveEntry.Open(); using FileStream output = new FileStream(executableDownloadPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 81920, useAsync: true); await input.CopyToAsync(output).ConfigureAwait(continueOnCapturedContext: false); } if (!IsVerifiedFile(executableDownloadPath, 134163456L, "BA242553F0FF60AD788069D5D376C1B4F7A2F3A3566416E0ED950CA7920DA5FA")) { throw new InvalidDataException("The extracted FFmpeg executable failed integrity verification."); } File.Move(executableDownloadPath, ffmpegPath); _validatedFfmpegPath = ffmpegPath; return ffmpegPath; } finally { TryDeleteFile(archivePath); TryDeleteFile(executableDownloadPath); } } finally { BinaryGate.Release(); } } private static async Task DownloadFileAsync(string url, string destinationPath) { using HttpResponseMessage response = await DownloadClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(continueOnCapturedContext: false); response.EnsureSuccessStatusCode(); using Stream input = await response.Content.ReadAsStreamAsync().ConfigureAwait(continueOnCapturedContext: false); using FileStream output = new FileStream(destinationPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 81920, useAsync: true); await input.CopyToAsync(output).ConfigureAwait(continueOnCapturedContext: false); } private static async Task RunYtDlpAsync(string binaryPath, string ffmpegPath, string outputPath, string normalizedUrl) { return await RunProcessAsync(binaryPath, BuildYtDlpArguments(ffmpegPath, outputPath, normalizedUrl)).ConfigureAwait(continueOnCapturedContext: false); } private static async Task RunProcessAsync(string binaryPath, string arguments) { Process process = new Process { StartInfo = new ProcessStartInfo { FileName = binaryPath, Arguments = arguments, UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 } }; try { if (!process.Start()) { throw new InvalidOperationException("The media helper did not start."); } Task outputTask = process.StandardOutput.ReadToEndAsync(); Task errorTask = process.StandardError.ReadToEndAsync(); Task waitTask = Task.Run(delegate { process.WaitForExit(); }); if (await Task.WhenAny(new Task[2] { waitTask, Task.Delay(ProcessTimeout) }).ConfigureAwait(continueOnCapturedContext: false) != waitTask) { try { if (!process.HasExited) { process.Kill(); } } catch { } await Task.WhenAny(new Task[2] { Task.WhenAll(waitTask, outputTask, errorTask), Task.Delay(TimeSpan.FromSeconds(2.0)) }).ConfigureAwait(continueOnCapturedContext: false); return new ProcessResult { ExitCode = -1, StandardOutput = GetCompletedTaskResult(outputTask), StandardError = GetCompletedTaskResult(errorTask), TimedOut = true }; } await Task.WhenAll(outputTask, errorTask).ConfigureAwait(continueOnCapturedContext: false); return new ProcessResult { ExitCode = process.ExitCode, StandardOutput = outputTask.Result, StandardError = errorTask.Result, TimedOut = false }; } finally { process.Dispose(); } } private static async Task DetectEncodedBordersAsync(string ffmpegPath, string videoPath) { ProcessResult processResult = await RunProcessAsync(ffmpegPath, BuildCropDetectionArguments(videoPath)).ConfigureAwait(continueOnCapturedContext: false); if (processResult.TimedOut) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)"[MonitorTakeover] FFmpeg timed out while checking the video frame for encoded borders."); } return null; } if (processResult.ExitCode != 0) { ManualLogSource log2 = TakeoverBootstrap.Log; if (log2 != null) { log2.LogWarning((object)("[MonitorTakeover] FFmpeg could not inspect the video frame for encoded borders: " + GetSafeError(processResult.StandardError))); } return null; } string input = processResult.StandardError ?? string.Empty; Match match = FrameSizePattern.Match(input); MatchCollection matchCollection = CropRectanglePattern.Matches(input); if (!match.Success || matchCollection.Count == 0) { return null; } Match match2 = matchCollection[matchCollection.Count - 1]; if (!TryParsePositiveInt(match.Groups["width"].Value, out var parsed) || !TryParsePositiveInt(match.Groups["height"].Value, out var parsed2) || !TryParsePositiveInt(match2.Groups["width"].Value, out var parsed3) || !TryParsePositiveInt(match2.Groups["height"].Value, out var parsed4) || !TryParseNonNegativeInt(match2.Groups["x"].Value, out var parsed5) || !TryParseNonNegativeInt(match2.Groups["y"].Value, out var parsed6)) { return null; } if (parsed3 > parsed || parsed4 > parsed2 || parsed5 > parsed - parsed3 || parsed6 > parsed2 - parsed4 || parsed3 < parsed / 5 || parsed4 < parsed2 / 5) { return null; } if (parsed - parsed3 < 8 && parsed2 - parsed4 < 8) { return null; } return new CropRegion { SourceWidth = parsed, SourceHeight = parsed2, Width = parsed3, Height = parsed4, X = parsed5, Y = parsed6 }; } private static string BuildCropDetectionArguments(string videoPath) { string value = (IsWindowsPlatform() ? "NUL" : "/dev/null"); return "-hide_banner -loglevel info -i " + QuoteProcessArgument(videoPath) + " -t 12 -vf " + QuoteProcessArgument("fps=2,cropdetect=limit=24:round=2:reset=0,showinfo") + " -an -f null " + QuoteProcessArgument(value); } private static string BuildFillFrameArguments(string inputPath, string outputPath, CropRegion crop) { string value = $"crop={crop.Width}:{crop.Height}:{crop.X}:{crop.Y}," + $"scale={crop.SourceWidth}:{crop.SourceHeight}:flags=lanczos,setsar=1"; return "-y -hide_banner -loglevel error -i " + QuoteProcessArgument(inputPath) + " -map 0:v:0 -map 0:a:0? -vf " + QuoteProcessArgument(value) + " -c:v libx264 -preset veryfast -crf 20 -pix_fmt yuv420p -c:a copy -movflags +faststart " + QuoteProcessArgument(outputPath); } private static bool TryParsePositiveInt(string value, out int parsed) { if (int.TryParse(value, out parsed)) { return parsed > 0; } return false; } private static bool TryParseNonNegativeInt(string value, out int parsed) { if (int.TryParse(value, out parsed)) { return parsed >= 0; } return false; } private static string BuildYtDlpArguments(string ffmpegPath, string outputPath, string normalizedUrl) { return "--ignore-config --no-playlist --no-progress --socket-timeout 15 --retries 2 --fragment-retries 2 --max-filesize 256M --merge-output-format mp4 --ffmpeg-location " + QuoteProcessArgument(ffmpegPath) + " --format " + QuoteProcessArgument("bestvideo[ext=mp4][height<=480][vcodec^=avc1]+bestaudio[ext=m4a]/best[ext=mp4][height<=480][vcodec^=avc1][acodec^=mp4a]") + " --output " + QuoteProcessArgument(outputPath) + " -- " + QuoteProcessArgument(normalizedUrl); } private static string QuoteProcessArgument(string value) { if (value == null) { return "\"\""; } StringBuilder stringBuilder = new StringBuilder(value.Length + 2); stringBuilder.Append('"'); int num = 0; foreach (char c in value) { switch (c) { case '\\': num++; break; case '"': stringBuilder.Append('\\', num * 2 + 1); stringBuilder.Append('"'); num = 0; break; default: stringBuilder.Append('\\', num); num = 0; stringBuilder.Append(c); break; } } stringBuilder.Append('\\', num * 2); stringBuilder.Append('"'); return stringBuilder.ToString(); } private static string GetCompletedTaskResult(Task task) { if (task.Status != TaskStatus.RanToCompletion) { return string.Empty; } return task.Result; } private static HttpClient CreateDownloadClient() { HttpClient httpClient = new HttpClient(); httpClient.Timeout = TimeSpan.FromMinutes(3.0); httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Y4NGZMonitorTakeover/1.0.1"); return httpClient; } private static string GetYtDlpBinaryName() { if (IsWindowsPlatform()) { return "yt-dlp.exe"; } return Environment.OSVersion.Platform switch { PlatformID.MacOSX => "yt-dlp_macos", PlatformID.Unix => "yt-dlp", _ => throw new PlatformNotSupportedException("yt-dlp is not available for this operating system."), }; } private static bool IsWindowsPlatform() { PlatformID platform = Environment.OSVersion.Platform; if (platform != PlatformID.Win32NT && platform != PlatformID.Win32S && platform != PlatformID.Win32Windows) { return platform == PlatformID.WinCE; } return true; } private static string FindExecutableOnPath(string executableName) { string environmentVariable = Environment.GetEnvironmentVariable("PATH"); if (string.IsNullOrWhiteSpace(environmentVariable)) { return null; } string[] array = environmentVariable.Split(Path.PathSeparator); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim().Trim('"'); if (text.Length == 0) { continue; } try { string path = Path.Combine(text, executableName); if (File.Exists(path)) { return Path.GetFullPath(path); } } catch { } } return null; } private static bool IsValidCachedVideo(string path) { try { FileInfo fileInfo = new FileInfo(path); if (!fileInfo.Exists || fileInfo.Length < 65536 || fileInfo.Length > 268435456) { return false; } byte[] array = new byte[12]; using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { if (fileStream.Read(array, 0, array.Length) != array.Length) { return false; } } return array[4] == 102 && array[5] == 116 && array[6] == 121 && array[7] == 112; } catch { return false; } } private static bool IsVerifiedFile(string path, long expectedLength, string expectedSha256) { try { FileInfo fileInfo = new FileInfo(path); return fileInfo.Exists && fileInfo.Length == expectedLength && string.Equals(ComputeFileSha256(path), expectedSha256, StringComparison.OrdinalIgnoreCase); } catch { return false; } } private static string ComputeTextSha256(string value) { using SHA256 sHA = SHA256.Create(); return ToHex(sHA.ComputeHash(Encoding.UTF8.GetBytes(value ?? string.Empty))); } private static string ComputeFileSha256(string path) { using SHA256 sHA = SHA256.Create(); using FileStream inputStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); return ToHex(sHA.ComputeHash(inputStream)); } private static string ToHex(byte[] bytes) { return BitConverter.ToString(bytes).Replace("-", string.Empty); } private static void DeleteWorkingVideoFiles(string videoDirectory, string cacheKey) { string[] files = Directory.GetFiles(videoDirectory, cacheKey + ".working*", SearchOption.TopDirectoryOnly); for (int i = 0; i < files.Length; i++) { TryDeleteFile(files[i]); } } private static void TryDeleteFile(string path) { try { if (File.Exists(path)) { File.Delete(path); } } catch { } } private static void EnsureExecutablePermission(string binaryPath) { if (Environment.OSVersion.Platform != PlatformID.Unix && Environment.OSVersion.Platform != PlatformID.MacOSX) { return; } Process process = new Process { StartInfo = new ProcessStartInfo { FileName = "chmod", Arguments = "+x " + QuoteProcessArgument(binaryPath), UseShellExecute = false, CreateNoWindow = true } }; try { if (!process.Start()) { throw new InvalidOperationException("chmod did not start."); } process.WaitForExit(); if (process.ExitCode != 0) { throw new IOException($"chmod exited with code {process.ExitCode}."); } } finally { process.Dispose(); } } private static bool HasRecognizedVideoPath(Uri uri, bool shortHost) { string text = uri.AbsolutePath.TrimEnd('/'); if (shortHost) { return text.Length > 1; } if (text.Equals("/watch", StringComparison.OrdinalIgnoreCase)) { return HasQueryParameter(uri.Query, "v"); } if (!text.StartsWith("/shorts/", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("/embed/", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("/live/", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("/v/", StringComparison.OrdinalIgnoreCase)) { return text.StartsWith("/clip/", StringComparison.OrdinalIgnoreCase); } return true; } private static bool HasQueryParameter(string query, string expectedName) { if (string.IsNullOrEmpty(query)) { return false; } string[] array = query.TrimStart('?').Split('&'); for (int i = 0; i < array.Length; i++) { int num = array[i].IndexOf('='); string a = ((num >= 0) ? array[i].Substring(0, num) : array[i]); string value = ((num >= 0) ? array[i].Substring(num + 1) : string.Empty); if (string.Equals(a, expectedName, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(value)) { return true; } } return false; } private static string GetSafeSourceLabel(string normalizedUrl) { if (!Uri.TryCreate(normalizedUrl, UriKind.Absolute, out Uri result)) { return "from the configured link"; } return result.IdnHost + result.AbsolutePath; } private static string GetSafeError(string message) { if (string.IsNullOrWhiteSpace(message)) { return "no diagnostic was returned"; } string[] array = message.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); string text = ((array.Length != 0) ? array[^1].Trim() : message.Trim()); if (text.IndexOf("http", StringComparison.OrdinalIgnoreCase) >= 0) { return "yt-dlp reported an HTTP or URL-related failure (URL omitted)"; } if (text.Length > 400) { text = text.Substring(0, 400) + "..."; } return text; } } } namespace Y4NGZCompany.ShipSystems.Takeover { internal static class MonitorTakeoverConfigMigration { internal const string DestinationFileName = "com.y4ngz.company.monitortakeover.cfg"; internal static void MigrateConfigSafely(ConfigFile config, ManualLogSource log) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown SplitConfigImporter.ImportOnceSafely(config, log, "Y4NGZMonitorTakeover", "com.y4ngz.company.monitortakeover.cfg", (SplitConfigImportSource[])(object)new SplitConfigImportSource[3] { new SplitConfigImportSource("com.y4ngz.company.cfg", (Func)MapLegacy, (Func)IsPotentiallyOwnedLegacySection), new SplitConfigImportSource("com.lguhud.contracthud.cfg", (Func)MapLegacy, (Func)IsPotentiallyOwnedLegacySection), new SplitConfigImportSource("com.y4ngz.lgumonitortakeover.cfg", (Func)MapLegacy, (Func)IsPotentiallyOwnedLegacySection) }); MonitorTakeoverConfigSchemaMigration.MigrateSafely(config, log); } private static SplitConfigTarget MapCanonical(string section, string key) { return MonitorTakeoverConfigSchemaMigration.MapNumbered(section, key); } private static SplitConfigTarget MapLegacy(string section, string key) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown SplitConfigTarget val = MapCanonical(section, key); if (val != null) { return val; } if (section == "Settings") { string text; switch (key) { case "Enabled": case "Takeover Duration": case "Orbit Delay": text = "General"; break; case "Mumble Volume": text = "Audio"; break; default: text = "Presentation"; break; } string text2 = ((key == "Override Fifth Monitor") ? "Override Additional Monitors" : key); return new SplitConfigTarget(text, text2); } if (section == "Diagnostics" && (key == "Verbose Logging" || key == "Dump Monitor Hierarchy On Orbit")) { return new SplitConfigTarget("Diagnostics", key); } return null; } private static bool IsPotentiallyOwnedLegacySection(string section) { if (!MonitorTakeoverConfigSchemaMigration.IsLegacyOwnedSection(section) && !(section == "Settings")) { return section == "Diagnostics"; } return true; } } internal static class MonitorTakeoverConfigSchemaMigration { internal const int CurrentSchemaVersion = 1; internal const string MigrationSection = "00 - Migration"; internal const string MigrationVersionKey = "ConfigSchemaVersion"; internal const string GeneralSection = "General"; internal const string MediaSection = "Media"; internal const string AudioSection = "Audio"; internal const string PresentationSection = "Presentation"; internal const string RewardsCeremonySection = "Rewards Ceremony"; internal const string DiagnosticsSection = "Diagnostics"; internal const string LegacyQuotaSectionPrefix = "70 - Quota Progression - Quota "; internal const string LegacyQuotaSharedSection = "70 - Quota Progression - Shared"; internal const string LegacyTakeoverSharedSection = "71 - Monitor Takeover - Shared"; internal const string LegacyPresentationSection = "72 - Monitor Takeover - Presentation"; internal const string LegacyEndingSection = "73 - Monitor Takeover - Ending"; internal const string LegacyDiagnosticsSection = "79 - Monitor Takeover - Diagnostics"; private static readonly Dictionary TakeoverSharedBuckets = new Dictionary(StringComparer.Ordinal) { ["Enabled"] = "General", ["Takeover Duration"] = "General", ["Orbit Delay"] = "General", ["DefaultMediaFile"] = "Media", ["LoopMedia"] = "Media", ["EnableMediaAudio"] = "Media", ["UseMediaLengthAsDuration"] = "Media", ["MumbleAudioFiles"] = "Audio", ["AlarmAudioFile"] = "Audio", ["RandomizeDialogue"] = "Presentation", ["RandomizeWithoutReplacement"] = "Presentation", ["EnableUnlockAnnouncement"] = "Rewards Ceremony", ["UnlockAnnouncementSeconds"] = "Rewards Ceremony", ["AnnounceOnBothMonitors"] = "Rewards Ceremony", ["RewardsCeremonyEnabled"] = "Rewards Ceremony", ["CeremonyMirrorBothMonitors"] = "Rewards Ceremony", ["CeremonyTypewriterSecondsPerChar"] = "Rewards Ceremony", ["CeremonyLineConfirmSting"] = "Rewards Ceremony", ["CeremonyRestoreStatic"] = "Rewards Ceremony" }; private static readonly Dictionary PresentationBuckets = new Dictionary(StringComparer.Ordinal) { ["Mumble Volume"] = "Audio", ["Typewriter Speed"] = "Presentation", ["Hide HUD"] = "Presentation", ["Dim Lights"] = "Presentation", ["Light Dim Intensity"] = "Presentation", ["Override Additional Monitors"] = "Presentation", ["Additional Monitor Name Tokens"] = "Presentation", ["Mask Man Tail Buffer"] = "Presentation" }; internal static string QuotaSection(int quota) { return "Quota " + quota.ToString(CultureInfo.InvariantCulture); } internal static SplitConfigTarget MapNumbered(string section, string key) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown if (section == null || key == null) { return null; } switch (section) { case "70 - Quota Progression - Shared": if (!(key == "ConfiguredMilestones")) { return new SplitConfigTarget("General", key); } return null; case "71 - Monitor Takeover - Shared": { if (TakeoverSharedBuckets.TryGetValue(key, out var value)) { return new SplitConfigTarget(value, key); } return new SplitConfigTarget("General", key); } case "72 - Monitor Takeover - Presentation": { if (PresentationBuckets.TryGetValue(key, out var value2)) { return new SplitConfigTarget(value2, key); } return new SplitConfigTarget("Presentation", key); } case "79 - Monitor Takeover - Diagnostics": return new SplitConfigTarget("Diagnostics", key); default: { int num = ParseLegacyQuotaNumber(section); if (num <= 0) { return null; } return new SplitConfigTarget(QuotaSection(num), key); } } } internal static bool IsLegacyOwnedSection(string section) { switch (section) { case "70 - Quota Progression - Shared": case "71 - Monitor Takeover - Shared": case "72 - Monitor Takeover - Presentation": case "73 - Monitor Takeover - Ending": case "79 - Monitor Takeover - Diagnostics": return true; default: return ParseLegacyQuotaNumber(section) > 0; } } private static int ParseLegacyQuotaNumber(string section) { if (section == null || !section.StartsWith("70 - Quota Progression - Quota ", StringComparison.Ordinal)) { return 0; } string text = section.Substring("70 - Quota Progression - Quota ".Length); if (text.Length != 2 || !char.IsDigit(text[0]) || !char.IsDigit(text[1]) || !int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return 0; } return result; } internal static void MigrateSafely(ConfigFile config, ManualLogSource log) { try { Migrate(config, log); } catch (Exception ex) { if (log != null) { log.LogError((object)("Monitor Takeover config schema migration did not finish (" + ex.GetType().Name + "); startup continues on the current config.")); } try { if (config != null) { config.Reload(); } } catch (Exception ex2) { if (log != null) { log.LogWarning((object)("Config reload after a failed Monitor Takeover schema migration also failed: " + ex2.GetType().Name)); } } } } private static void Migrate(ConfigFile config, ManualLogSource log) { //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Expected O, but got Unknown //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown string path = Path.Combine(Paths.ConfigPath, "com.y4ngz.company.monitortakeover.cfg"); Dictionary dictionary = ReadConfigValues(path); if (GetSchemaVersion(dictionary) >= 1) { return; } Dictionary dictionary2 = new Dictionary(); foreach (KeyValuePair item in dictionary) { if (!IsLegacyOwnedSection(item.Key.Section)) { dictionary2[item.Key] = item.Value; } } int num = 0; int num2 = 0; foreach (KeyValuePair item2 in dictionary) { if (!IsLegacyOwnedSection(item2.Key.Section)) { continue; } SplitConfigTarget val = MapNumbered(item2.Key.Section, item2.Key.Key); if (val == null) { num2++; continue; } ConfigDefinition key = new ConfigDefinition(val.Section, val.Key); if (dictionary2.ContainsKey(key)) { num2++; continue; } dictionary2[key] = item2.Value; num++; } dictionary2[new ConfigDefinition("00 - Migration", "ConfigSchemaVersion")] = 1.ToString(CultureInfo.InvariantCulture); WriteConfigValuesAtomic(path, dictionary2); if (config != null) { config.Reload(); } if (log != null) { log.LogInfo((object)($"Monitor Takeover config schema v{1} migration: " + $"moved={num}, dropped={num2}. Values already in the new sections were kept.")); } } private static int GetSchemaVersion(Dictionary values) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if (!values.TryGetValue(new ConfigDefinition("00 - Migration", "ConfigSchemaVersion"), out var value) || !int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return 0; } return result; } private static void WriteConfigValuesAtomic(string path, Dictionary values) { string directoryName = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } string text = path + "." + Guid.NewGuid().ToString("N") + ".tmp"; string text2 = path + $".pre-schema-v{1}-" + DateTime.UtcNow.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture) + ".bak"; try { WriteConfigValues(text, values); if (!File.Exists(path)) { File.Move(text, path); return; } try { File.Replace(text, path, text2, ignoreMetadataErrors: true); } catch (PlatformNotSupportedException) { ReplaceWithRollback(text, path, text2); } catch (IOException) { ReplaceWithRollback(text, path, text2); } } finally { if (File.Exists(text)) { File.Delete(text); } } } private static void ReplaceWithRollback(string tempPath, string path, string backupPath) { File.Copy(path, backupPath, overwrite: false); string text = path + ".rollback-" + Guid.NewGuid().ToString("N"); File.Move(path, text); try { File.Move(tempPath, path); File.Delete(text); } catch { if (File.Exists(path)) { File.Delete(path); } File.Move(text, path); throw; } } private static void WriteConfigValues(string path, Dictionary values) { SortedDictionary> sortedDictionary = new SortedDictionary>(StringComparer.Ordinal); foreach (KeyValuePair value2 in values) { string key = value2.Key.Section ?? string.Empty; if (!sortedDictionary.TryGetValue(key, out var value)) { value = (sortedDictionary[key] = new SortedDictionary(StringComparer.Ordinal)); } value[value2.Key.Key] = value2.Value; } using StreamWriter streamWriter = new StreamWriter(path, append: false); streamWriter.WriteLine("## Y4NGZ Monitor Takeover configuration. Legacy section names were normalized automatically."); foreach (KeyValuePair> item in sortedDictionary) { streamWriter.WriteLine(); streamWriter.Write('['); streamWriter.Write(item.Key); streamWriter.WriteLine(']'); foreach (KeyValuePair item2 in item.Value) { streamWriter.Write(item2.Key); streamWriter.Write(" = "); streamWriter.WriteLine(item2.Value); } } } private static Dictionary ReadConfigValues(string path) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown Dictionary dictionary = new Dictionary(); if (!File.Exists(path)) { return dictionary; } string text = null; string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2[0] == '#' || text2[0] == ';') { continue; } if (text2[0] == '[') { text = ((text2[text2.Length - 1] == ']') ? text2.Substring(1, text2.Length - 2).Trim() : null); } else { if (text == null) { continue; } int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); if (text3.Length != 0) { dictionary[new ConfigDefinition(text, text3)] = text2.Substring(num + 1).Trim(); } } } } return dictionary; } } [HarmonyPatch(typeof(TimeOfDay), "SetNewProfitQuota")] internal static class QuotaRolloverPublisher { private const int PublishPriority = -10; private static int _profitQuotaBefore; private static int _quotaFulfilledBefore; [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(TimeOfDay __instance) { if (!((Object)(object)__instance == (Object)null)) { _profitQuotaBefore = __instance.profitQuota; _quotaFulfilledBefore = __instance.quotaFulfilled; } } [HarmonyPostfix] [HarmonyPriority(-10)] private static void Postfix(TimeOfDay __instance) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null) { return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && singleton.IsListening && !singleton.IsServer) { return; } int num = default(int); bool flag = default(bool); QuotaRolloverOutcomeStaging.Consume(ref num, ref flag); QuotaRolloverFinalized val = default(QuotaRolloverFinalized); ((QuotaRolloverFinalized)(ref val))..ctor(__instance.timesFulfilledQuota, _profitQuotaBefore, _quotaFulfilledBefore, __instance.profitQuota, __instance.daysUntilDeadline, num, flag); if (QuotaRolloverNotifications.PublishQuotaRolloverFinalized(val)) { ModuleLog.Takeover.LogInfo((object)($"[QuotaRollover] Quota rollover {val.CompletedQuotaCount} published " + $"(quota {val.ProfitQuotaBefore} -> {val.ProfitQuotaAfter}, " + $"objective delta {val.ObjectiveOutcomeDelta}, " + $"counted={val.OutcomeCountedTowardQuota}).")); return; } if (num != 0 || flag) { QuotaRolloverOutcomeStaging.ReportObjectiveOutcome(num, flag); } ModuleLog.Takeover.LogInfo((object)("[QuotaRollover] Duplicate quota rollover publish suppressed for completed " + $"quota {val.CompletedQuotaCount}.")); } } internal static class CRTStatic { internal static Texture2D CreateSolidTexture(Color color) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false); val.SetPixel(0, 0, color); val.Apply(); return val; } internal static Texture2D CreateNoiseTexture(int width, int height) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(width, height, (TextureFormat)4, false); ((Texture)val).filterMode = (FilterMode)0; Color[] array = (Color[])(object)new Color[width * height]; for (int i = 0; i < array.Length; i++) { float value = Random.value; array[i] = new Color(value, value, value, 1f); } val.SetPixels(array); val.Apply(); return val; } internal static AudioClip CreateBurstClip() { int num = 44100; int num2 = (int)((float)num * 0.35f); float[] array = new float[num2]; for (int i = 0; i < num2; i++) { float num3 = 1f; if ((float)i > (float)num2 * 0.75f) { num3 = 1f - ((float)i - (float)num2 * 0.75f) / ((float)num2 * 0.25f); } array[i] = (Random.value * 2f - 1f) * 0.45f * num3; } AudioClip obj = AudioClip.Create("Y4NGZ_Static", num2, 1, num, false); obj.SetData(array, 0); return obj; } } internal class MonitorDebugger : MonoBehaviour { internal static void DumpAll() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Invalid comparison between Unknown and I4 //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_070a: Unknown result type (might be due to invalid IL or missing references) //IL_0711: Expected O, but got Unknown StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("========== Y4NGZ MONITOR DEBUGGER =========="); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { TakeoverBootstrap.Log.LogWarning((object)"StartOfRound.Instance is null — are you in-game?"); return; } stringBuilder.AppendLine("\n--- screenLevelVideoReel (VideoPlayer) ---"); VideoPlayer screenLevelVideoReel = instance.screenLevelVideoReel; if ((Object)(object)screenLevelVideoReel == (Object)null) { stringBuilder.AppendLine(" NULL"); } else { stringBuilder.AppendLine($" renderMode : {screenLevelVideoReel.renderMode}"); stringBuilder.AppendLine(" targetTexture : " + (((Object)(object)screenLevelVideoReel.targetTexture != (Object)null) ? ((Object)screenLevelVideoReel.targetTexture).name : "null")); stringBuilder.AppendLine(" clip : " + (((Object)(object)screenLevelVideoReel.clip != (Object)null) ? ((Object)screenLevelVideoReel.clip).name : "null")); stringBuilder.AppendLine($" isPlaying : {screenLevelVideoReel.isPlaying}"); stringBuilder.AppendLine(" gameObject path : " + GetPath(((Component)screenLevelVideoReel).gameObject)); if ((int)screenLevelVideoReel.renderMode == 3) { Renderer val = ((Component)screenLevelVideoReel).GetComponent() ?? ((Component)screenLevelVideoReel).GetComponentInChildren(); stringBuilder.AppendLine(" MaterialOverride renderer: " + (((Object)(object)val != (Object)null) ? GetPath(((Component)val).gameObject) : "none on same GO")); } } stringBuilder.AppendLine("\n--- mapScreen (ManualCameraRenderer) ---"); ManualCameraRenderer mapScreen = instance.mapScreen; if ((Object)(object)mapScreen == (Object)null) { stringBuilder.AppendLine(" NULL"); } else { stringBuilder.AppendLine(" gameObject path : " + GetPath(((Component)mapScreen).gameObject)); stringBuilder.AppendLine(" cam : " + (((Object)(object)mapScreen.cam != (Object)null) ? ((Object)mapScreen.cam).name : "null")); if ((Object)(object)mapScreen.cam != (Object)null) { stringBuilder.AppendLine(" cam.targetTexture: " + (((Object)(object)mapScreen.cam.targetTexture != (Object)null) ? ((Object)mapScreen.cam.targetTexture).name : "null")); } stringBuilder.AppendLine(" mapCamera : " + (((Object)(object)mapScreen.mapCamera != (Object)null) ? ((Object)mapScreen.mapCamera).name : "null")); if ((Object)(object)mapScreen.mapCamera != (Object)null) { stringBuilder.AppendLine(" mapCamera.targetTexture: " + (((Object)(object)mapScreen.mapCamera.targetTexture != (Object)null) ? ((Object)mapScreen.mapCamera.targetTexture).name : "null")); } } stringBuilder.AppendLine("\n--- upperMonitorsCanvas ---"); GameObject upperMonitorsCanvas = instance.upperMonitorsCanvas; if ((Object)(object)upperMonitorsCanvas == (Object)null) { stringBuilder.AppendLine(" NULL"); } else { stringBuilder.AppendLine(" path : " + GetPath(upperMonitorsCanvas)); stringBuilder.AppendLine(" parent : " + (((Object)(object)upperMonitorsCanvas.transform.parent != (Object)null) ? GetPath(((Component)upperMonitorsCanvas.transform.parent).gameObject) : "none")); Canvas[] componentsInChildren = upperMonitorsCanvas.GetComponentsInChildren(true); stringBuilder.AppendLine($" child Canvas count: {componentsInChildren.Length}"); Canvas[] array = componentsInChildren; foreach (Canvas val2 in array) { stringBuilder.AppendLine($" Canvas: {GetPath(((Component)val2).gameObject)} renderMode={val2.renderMode}"); } if ((Object)(object)upperMonitorsCanvas.transform.parent != (Object)null) { MeshRenderer[] componentsInChildren2 = ((Component)upperMonitorsCanvas.transform.parent).GetComponentsInChildren(true); stringBuilder.AppendLine($" MeshRenderers in parent ({((Object)upperMonitorsCanvas.transform.parent).name}): {componentsInChildren2.Length}"); MeshRenderer[] array2 = componentsInChildren2; foreach (MeshRenderer val3 in array2) { Material sharedMaterial = ((Renderer)val3).sharedMaterial; string text = (((Object)(object)((sharedMaterial != null) ? sharedMaterial.mainTexture : null) != (Object)null) ? (((object)sharedMaterial.mainTexture).GetType().Name + ":" + ((Object)sharedMaterial.mainTexture).name) : "no mainTexture"); stringBuilder.AppendLine(" " + GetPath(((Component)val3).gameObject) + " mat=" + ((sharedMaterial != null) ? ((Object)sharedMaterial).name : null) + " tex=" + text); } } } stringBuilder.AppendLine("\n--- Ship MeshRenderers with RenderTexture mainTexture ---"); GameObject val4 = GameObject.Find("Environment/HangarShip") ?? GameObject.Find("HangarShip"); if ((Object)(object)val4 == (Object)null) { stringBuilder.AppendLine(" HangarShip not found in scene"); } else { int num = 0; MeshRenderer[] array2 = val4.GetComponentsInChildren(true); foreach (MeshRenderer val5 in array2) { Material sharedMaterial2 = ((Renderer)val5).sharedMaterial; Texture obj = ((sharedMaterial2 != null) ? sharedMaterial2.mainTexture : null); RenderTexture val6 = (RenderTexture)(object)((obj is RenderTexture) ? obj : null); if (val6 != null) { stringBuilder.AppendLine(" " + GetPath(((Component)val5).gameObject)); stringBuilder.AppendLine($" material={((Object)((Renderer)val5).sharedMaterial).name} RT={((Object)val6).name} ({((Texture)val6).width}x{((Texture)val6).height})"); num++; } } if (num == 0) { stringBuilder.AppendLine(" (none found)"); } } stringBuilder.AppendLine("\n--- Top monitor UI components ---"); LogUIComponent(stringBuilder, "profitQuotaMonitorBGImage", (Component)(object)instance.profitQuotaMonitorBGImage); LogUIComponent(stringBuilder, "profitQuotaMonitorText", (Component)(object)instance.profitQuotaMonitorText); LogUIComponent(stringBuilder, "deadlineMonitorBGImage", (Component)(object)instance.deadlineMonitorBGImage); LogUIComponent(stringBuilder, "deadlineMonitorText", (Component)(object)instance.deadlineMonitorText); stringBuilder.AppendLine("\n--- HUDContainer ---"); HUDManager instance2 = HUDManager.Instance; if ((Object)(object)instance2?.HUDContainer != (Object)null) { stringBuilder.AppendLine($" path: {GetPath(instance2.HUDContainer)} active={instance2.HUDContainer.activeSelf}"); } else { stringBuilder.AppendLine(" HUDManager or HUDContainer is null"); } stringBuilder.AppendLine("\n--- MonitorWall ALL MeshRenderers ---"); GameObject val7 = GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall"); if ((Object)(object)val7 == (Object)null) { stringBuilder.AppendLine(" MonitorWall not found!"); } else { MeshRenderer[] array2 = val7.GetComponentsInChildren(true); foreach (MeshRenderer val8 in array2) { Material sharedMaterial3 = ((Renderer)val8).sharedMaterial; string text2 = (((Object)(object)((sharedMaterial3 != null) ? sharedMaterial3.mainTexture : null) != (Object)null) ? (((object)sharedMaterial3.mainTexture).GetType().Name + ":" + ((Object)sharedMaterial3.mainTexture).name) : "null"); string text3 = "n/a"; try { MaterialPropertyBlock val9 = new MaterialPropertyBlock(); ((Renderer)val8).GetPropertyBlock(val9); Texture texture = val9.GetTexture("_MainTex"); text3 = (((Object)(object)texture != (Object)null) ? ("MPB:" + ((Object)texture).name) : "no MPB"); } catch { } stringBuilder.AppendLine(" " + GetPath(((Component)val8).gameObject)); stringBuilder.AppendLine(" sharedMat=" + ((sharedMaterial3 != null) ? ((Object)sharedMaterial3).name : null) + " sharedTex=" + text2 + " instCheck=" + text3); } } stringBuilder.AppendLine("\n--- shared small-monitor face map ---"); stringBuilder.AppendLine(" " + ShipSystemsServices.DescribeMonitorFaceMap()); stringBuilder.AppendLine("\n--- monitor-row runtime ---"); stringBuilder.AppendLine(" " + ShipSystemsServices.DescribeMonitorRow()); stringBuilder.AppendLine("\n========== END DEBUGGER =========="); TakeoverBootstrap.Log.LogInfo((object)stringBuilder.ToString()); } private static void LogUIComponent(StringBuilder sb, string label, Component c) { //IL_0060: 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) if ((Object)(object)c == (Object)null) { sb.AppendLine(" " + label + ": NULL"); return; } sb.AppendLine(" " + label + ":"); sb.AppendLine(" path : " + GetPath(c.gameObject)); sb.AppendLine($" world position: {c.transform.position}"); sb.AppendLine($" enabled : {((Behaviour)c).enabled}"); } private static string GetPath(GameObject go) { if ((Object)(object)go == (Object)null) { return "(null)"; } string text = ((Object)go).name; Transform parent = go.transform.parent; int num = 0; while ((Object)(object)parent != (Object)null && num < 8) { text = ((Object)parent).name + "/" + text; parent = parent.parent; num++; } return text; } } [BepInPlugin("com.y4ngz.company.monitortakeover", "Monitor Takeover", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class MonitorTakeoverPlugin : BaseUnityPlugin { public const string Guid = "com.y4ngz.company.monitortakeover"; public const string Name = "Monitor Takeover"; public const string Version = "1.0.0"; public const int MonitorTakeoverProtocolRevision = 1; internal const string HarmonyId = "com.y4ngz.company.monitortakeover"; internal const string HostObjectName = "Y4NGZMonitorTakeover_Host"; private Harmony _harmony; private MonitorTakeoverHost _host; private bool _applicationQuitting; internal static MonitorTakeoverPlugin Instance { get; private set; } internal static MonoBehaviour CoroutineHost { get; private set; } private void Awake() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown Instance = this; ModuleLog.Takeover = ((BaseUnityPlugin)this).Logger; ModuleTickRegistry.WarningSink = delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }; SplitAssemblyDiagnostics.Report(((BaseUnityPlugin)this).Logger); MonitorTakeoverConfigMigration.MigrateConfigSafely(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); GameObject val = new GameObject("Y4NGZMonitorTakeover_Host") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)(object)val); _host = val.AddComponent(); CoroutineHost = (MonoBehaviour)(object)_host; ModuleHandshakeService.Publish("monitortakeover", 1, "1.0.0"); _harmony = new Harmony("com.y4ngz.company.monitortakeover"); Initialize(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger, _harmony, InitializeModule); _host.Activate(_harmony, ((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Monitor Takeover v1.0.0 loaded."); } private void OnApplicationQuit() { _applicationQuitting = true; } private void OnDestroy() { if (MonitorTakeoverHost.HasTornDown) { return; } if ((Object)(object)_host != (Object)null && _host.IsActivated) { if (_applicationQuitting) { ((BaseUnityPlugin)this).Logger.LogDebug((object)"[Y4NGZMonitorTakeover] Plugin component destroyed during application quit; 'Y4NGZMonitorTakeover_Host' is still alive and owns the shutdown."); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Y4NGZMonitorTakeover] The plugin component's GameObject was destroyed " + $"mid-session (frame={Time.frameCount}; BepInEx [Chainloader] " + "HideManagerGameObject is false, so BepInEx_Manager is destroyable by any mod's Find-based cleanup). 'Y4NGZMonitorTakeover_Host' is alive, so nothing was torn down: the tick registrations and every Harmony patch this plugin applied remain ACTIVE and gameplay continues normally.")); } } else { MonitorTakeoverHost.TearDown(_harmony, ((BaseUnityPlugin)this).Logger); } } internal static void Initialize(ConfigFile config, ManualLogSource logger, Harmony harmony, Action isolate) { harmony.PatchAll(typeof(QuotaRolloverPublisher)); QuotaProgressionRegistry.Initialize(config, logger); harmony.PatchAll(typeof(QuotaProgressionUnlockPatches)); isolate("Monitor Takeover", delegate { TakeoverBootstrap.Initialize(config, logger, harmony); }); ModuleTickRegistry.RegisterTick("com.y4ngz.company.monitortakeover", 100, "QuotaProgressionRegistry", (Action)QuotaProgressionRegistry.Tick); } internal static void InitializeModule(string label, Action initialize) { try { initialize(); } catch (Exception arg) { ManualLogSource takeover = ModuleLog.Takeover; if (takeover != null) { takeover.LogError((object)$"Y4NGZMonitorTakeover module '{label}' failed to initialize: {arg}"); } } } } internal sealed class MonitorTakeoverHost : MonoBehaviour { private static bool _tornDown; private Harmony _harmony; private ManualLogSource _log; private bool _activated; private bool _applicationQuitting; internal static bool HasTornDown => _tornDown; internal bool IsActivated => _activated; internal void Activate(Harmony harmony, ManualLogSource log) { _harmony = harmony; _log = log; _activated = true; } private void LateUpdate() { if (_activated) { ModuleTickRegistry.Tick(Time.frameCount); } } private void OnApplicationQuit() { _applicationQuitting = true; } private void OnDestroy() { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (!_activated) { return; } if (!_applicationQuitting) { ManualLogSource log = _log; if (log != null) { string[] obj = new string[5] { "[Y4NGZMonitorTakeover] Host destroyed outside application shutdown ", string.Format("(frame={0}, host='{1}', ", Time.frameCount, ((Object)(object)((Component)this).gameObject != (Object)null) ? ((Object)((Component)this).gameObject).name : ""), "scene='", null, null }; object obj2; if (!((Object)(object)((Component)this).gameObject != (Object)null)) { obj2 = ""; } else { Scene scene = ((Component)this).gameObject.scene; obj2 = ((Scene)(ref scene)).name; } obj[3] = (string)obj2; obj[4] = "'). Quota progression and every Harmony patch this plugin applied are going down with it."; log.LogError((object)string.Concat(obj)); } } TearDown(_harmony, _log); } internal static void TearDown(Harmony harmony, ManualLogSource log) { if (_tornDown) { return; } _tornDown = true; ModuleTickRegistry.RunTeardown(new string[1] { "com.y4ngz.company.monitortakeover" }); if (harmony == null) { return; } try { harmony.UnpatchSelf(); } catch (Exception ex) { if (log != null) { log.LogWarning((object)("Y4NGZMonitorTakeover shutdown warning: UnpatchSelf failed for Harmony id '" + harmony.Id + "': " + ex.Message)); } } } } internal static class OpenBodyCamsCompat { private const string PluginGuid = "Zaggy1024.OpenBodyCams"; private static bool _resolved; private static bool _present; private static Behaviour _bodyCamBehaviour; private static bool _wasEnabled; private static GameObject _overlayMeshGO; private static bool _overlayWasActive; private static bool _announcementSuspended; private static bool _announcementWasEnabled; private static bool _announcementOverlayWasActive; private static void Resolve() { _resolved = true; try { if (!Chainloader.PluginInfos.ContainsKey("Zaggy1024.OpenBodyCams")) { return; } GameObject val = GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall"); if ((Object)(object)val == (Object)null) { TakeoverBootstrap.Log.LogInfo((object)"[OBCCompat] OpenBodyCams loaded but MonitorWall not found at resolve time."); return; } Type type = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (!(assembly.GetName().Name != "OpenBodyCams")) { type = assembly.GetType("OpenBodyCams.Components.BodyCamComponent", throwOnError: false) ?? assembly.GetType("OpenBodyCams.BodyCamComponent", throwOnError: false); if (type != null) { break; } } } if (type == null) { TakeoverBootstrap.Log.LogWarning((object)"[OBCCompat] BodyCamComponent type not found in OpenBodyCams assembly."); return; } Component componentInChildren = val.GetComponentInChildren(type, true); Behaviour val2 = (Behaviour)(object)((componentInChildren is Behaviour) ? componentInChildren : null); if (val2 == null) { TakeoverBootstrap.Log.LogInfo((object)"[OBCCompat] No BodyCamComponent under MonitorWall — nothing to suppress."); return; } _bodyCamBehaviour = val2; _present = true; TakeoverBootstrap.Log.LogInfo((object)("[OBCCompat] Detected OpenBodyCams BodyCamComponent on '" + ((Object)((Component)val2).gameObject).name + "'.")); _overlayMeshGO = FindOverlayMeshUnder(val.transform); if ((Object)(object)_overlayMeshGO != (Object)null) { TakeoverBootstrap.Log.LogInfo((object)("[OBCCompat] Detected OBC overlay mesh: " + ((Object)_overlayMeshGO).name)); } } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[OBCCompat] Resolve failed: " + ex.Message)); } } private static GameObject FindOverlayMeshUnder(Transform parent) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown foreach (Transform item in parent) { Transform val = item; if (!((Object)(object)val == (Object)null)) { if (((Object)val).name != null && ((Object)val).name.StartsWith("BodyCamOverlayMesh", StringComparison.Ordinal)) { return ((Component)val).gameObject; } GameObject val2 = FindOverlayMeshUnder(val); if ((Object)(object)val2 != (Object)null) { return val2; } } } return null; } internal static void Suppress() { if (!_resolved || (_present && (Object)(object)_bodyCamBehaviour == (Object)null)) { _resolved = false; _present = false; _overlayMeshGO = null; Resolve(); } if (!_present || (Object)(object)_bodyCamBehaviour == (Object)null) { return; } try { _wasEnabled = _bodyCamBehaviour.enabled; if (_wasEnabled) { _bodyCamBehaviour.enabled = false; TakeoverBootstrap.Log.LogInfo((object)("[OBCCompat] Suppressed OpenBodyCams BodyCamComponent on '" + ((Object)((Component)_bodyCamBehaviour).gameObject).name + "'.")); } if ((Object)(object)_overlayMeshGO != (Object)null) { _overlayWasActive = _overlayMeshGO.activeSelf; if (_overlayWasActive) { _overlayMeshGO.SetActive(false); TakeoverBootstrap.Log.LogInfo((object)"[OBCCompat] Hid OBC overlay mesh under MonitorWall."); } } } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[OBCCompat] Suppress failed: " + ex.Message)); } } internal static void Restore() { if (!_present || (Object)(object)_bodyCamBehaviour == (Object)null) { return; } try { if ((Object)(object)_overlayMeshGO != (Object)null && _overlayWasActive && !_overlayMeshGO.activeSelf) { _overlayMeshGO.SetActive(true); TakeoverBootstrap.Log.LogInfo((object)"[OBCCompat] Restored OBC overlay mesh under MonitorWall."); } if (_wasEnabled && !_bodyCamBehaviour.enabled) { _bodyCamBehaviour.enabled = true; TakeoverBootstrap.Log.LogInfo((object)("[OBCCompat] Restored OpenBodyCams BodyCamComponent on '" + ((Object)((Component)_bodyCamBehaviour).gameObject).name + "'.")); } } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[OBCCompat] Restore failed: " + ex.Message)); } } internal static void SuspendForAnnouncement() { if (_announcementSuspended) { return; } if (!_resolved) { Resolve(); } if (!_present || (Object)(object)_bodyCamBehaviour == (Object)null) { return; } try { _announcementWasEnabled = _bodyCamBehaviour.enabled; if (_announcementWasEnabled) { _bodyCamBehaviour.enabled = false; } _announcementOverlayWasActive = (Object)(object)_overlayMeshGO != (Object)null && _overlayMeshGO.activeSelf; if (_announcementOverlayWasActive) { _overlayMeshGO.SetActive(false); } _announcementSuspended = true; TakeoverBootstrap.Log.LogInfo((object)"[OBCCompat] Suspended OpenBodyCams for the unlock announcement."); } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[OBCCompat] Announcement suspend failed: " + ex.Message)); } } internal static void RestoreForAnnouncement() { if (!_announcementSuspended) { return; } _announcementSuspended = false; if ((Object)(object)_bodyCamBehaviour == (Object)null) { return; } try { if (_announcementOverlayWasActive && (Object)(object)_overlayMeshGO != (Object)null && !_overlayMeshGO.activeSelf) { _overlayMeshGO.SetActive(true); } if (_announcementWasEnabled && !_bodyCamBehaviour.enabled) { _bodyCamBehaviour.enabled = true; } TakeoverBootstrap.Log.LogInfo((object)"[OBCCompat] Restored OpenBodyCams after the unlock announcement."); } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[OBCCompat] Announcement restore failed: " + ex.Message)); } } } internal static class Patches { private static bool _rolloverSubscribed; private static readonly QuotaRolloverDedup RolloverDedup = new QuotaRolloverDedup(); internal static void OnQuotaRolloverFinalized(QuotaRolloverFinalized rollover) { HandleQuotaRollover("server"); } internal static void EnsureRolloverSubscription() { if (!_rolloverSubscribed) { _rolloverSubscribed = true; QuotaRolloverNotifications.QuotaRolloverFinalized += OnQuotaRolloverFinalized; } } [HarmonyPatch(typeof(TimeOfDay), "SyncNewProfitQuotaClientRpc")] [HarmonyPostfix] private static void OnSyncNewProfitQuotaClientRpc() { HandleQuotaRollover("client"); } internal static void ResetQuotaRolloverGuard() { RolloverDedup.Reset(); } private static void HandleQuotaRollover(string source) { string text; try { text = QuotaRolloverDedup.BuildKey(GameNetworkManager.Instance?.currentSaveFileName, ((Object)(object)TimeOfDay.Instance != (Object)null) ? TimeOfDay.Instance.timesFulfilledQuota : (-1)); } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[LGUMonitorTakeover] Quota rollover key failed: " + ex.Message)); text = null; } if (!RolloverDedup.TryClaim(text)) { TakeoverBootstrap.Log.LogInfo((object)("[LGUMonitorTakeover] Duplicate quota rollover hook (" + source + ") suppressed for '" + text + "'.")); } else { QuotaTakeoverHandoff.QuotaJustCompleted = true; TakeoverBootstrap.Log.LogInfo((object)("[LGUMonitorTakeover] Quota fulfilled (" + source + ") — takeover queued.")); CheckMaskManTrigger(source); } } private static void CheckMaskManTrigger(string source) { try { TimeOfDay instance = TimeOfDay.Instance; int num = (((Object)(object)instance != (Object)null) ? instance.timesFulfilledQuota : (-1)); bool flag = false; try { string text = GameNetworkManager.Instance?.currentSaveFileName; if (!string.IsNullOrEmpty(text)) { flag = ES3.Load("Y4NGZ_MaskManFired", text, false); } } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[LGUMonitorTakeover] Failed to read Y4NGZ_MaskManFired from save: " + ex.Message)); } TakeoverBootstrap.Log.LogInfo((object)($"[LGUMonitorTakeover] MaskMan check ({source}): timesFulfilledQuota={num} " + $"alreadyFiredThisSave={flag}")); if ((Object)(object)instance != (Object)null && num == 3 && !flag) { QuotaTakeoverHandoff.MaskManPending = true; QuotaTakeoverHandoff.QuotaJustCompleted = false; TakeoverBootstrap.Log.LogInfo((object)"[LGUMonitorTakeover] Quota 3 completed — Mask Man takeover queued (one-shot per save). Y4NGZ takeover suppressed for this orbit."); } } catch (Exception ex2) { TakeoverBootstrap.Log.LogWarning((object)("[LGUMonitorTakeover] CheckMaskManTrigger (" + source + ") failed: " + ex2.Message)); } } [HarmonyPatch(typeof(StartOfRound), "SetShipReadyToLand")] [HarmonyPostfix] private static void OnSetShipReadyToLand(StartOfRound __instance) { ConfigEntry cfgDumpMonitorHierarchyOnOrbit = TakeoverBootstrap.CfgDumpMonitorHierarchyOnOrbit; if (cfgDumpMonitorHierarchyOnOrbit != null && cfgDumpMonitorHierarchyOnOrbit.Value) { MonitorDebugger.DumpAll(); } bool num = QuotaTakeoverDebugController.ConsumeQueuedSkipOrbitDelay(); bool flag = TryStartPendingTakeover(num, num); if (num && !flag) { QuotaUnlockAnnouncement.ClearDebugReplay(); } } internal static bool TryStartPendingTakeover(bool skipOrbitDelay = false, bool debugForced = false) { if (QuotaTakeoverHandoff.MaskManPending) { QuotaTakeoverHandoff.MaskManPending = false; TakeoverManager.EnsureInstance(); if ((Object)(object)TakeoverManager.Instance == (Object)null) { return false; } TakeoverBootstrap.Log.LogInfo((object)"[LGUMonitorTakeover] Starting MASK MAN takeover."); TakeoverManager.Instance.BeginMaskManTakeover(skipOrbitDelay, !debugForced); return true; } if (!QuotaTakeoverHandoff.QuotaJustCompleted) { return false; } QuotaTakeoverHandoff.QuotaJustCompleted = false; TakeoverManager.EnsureInstance(); if ((Object)(object)TakeoverManager.Instance == (Object)null) { return false; } TakeoverBootstrap.Log.LogInfo((object)"[LGUMonitorTakeover] Starting takeover coroutine."); TakeoverManager.Instance.BeginTakeover(skipOrbitDelay); return true; } [HarmonyPatch(typeof(GameNetworkManager), "Disconnect")] [HarmonyPostfix] private static void OnDisconnect() { ResetQuotaRolloverGuard(); TakeoverManager.Instance?.ForceRestore("disconnect"); TakeoverMonitorOwnership.ResetForNewSession("disconnect"); QuotaTakeoverDebugController.ResetPending(); } [HarmonyPatch(typeof(StartOfRound), "ShipLeave")] [HarmonyPostfix] private static void OnShipLeave() { TakeoverManager.Instance?.ForceRestore("ShipLeave"); } [HarmonyPatch(typeof(StartOfRound), "ShipLeaveAutomatically")] [HarmonyPostfix] private static void OnShipLeaveAutomatically() { TakeoverManager.Instance?.ForceRestore("ShipLeaveAutomatically"); } [HarmonyPatch(typeof(GameNetworkManager), "ResetSavedGameValues")] [HarmonyPostfix] private static void OnResetSavedGameValues() { ResetQuotaRolloverGuard(); TakeoverManager.Instance?.ForceRestore("reset saved game values"); TakeoverMonitorOwnership.ResetForNewSession("reset saved game values"); QuotaTakeoverDebugController.ResetPending(); } } internal sealed class QuotaRolloverDedup { private string _handledKey; internal static string BuildKey(string saveFileName, int timesFulfilledQuota) { return (string.IsNullOrEmpty(saveFileName) ? "unsaved" : saveFileName) + ":" + timesFulfilledQuota; } internal bool TryClaim(string key) { if (key == null) { return true; } if (_handledKey != null && string.Equals(_handledKey, key, StringComparison.Ordinal)) { return false; } _handledKey = key; return true; } internal void Reset() { _handledKey = null; } } [DisallowMultipleComponent] internal sealed class QuotaTakeoverDebugController : MonoBehaviour { private const string ForceQuotaMessage = "Y4NGZCompany.ForceQuotaCompletion.v1"; private static QuotaTakeoverDebugController _instance; private NetworkManager _registeredNetworkManager; private int _nextSequence = 1; private int _lastAppliedSequence = -1; private bool _takeoverQueuedUntilOrbit; private int _queuedCompletedQuota; private float _nextQueuedBroadcastAt; internal static void EnsureAttached(GameObject owner) { if (!((Object)(object)_instance != (Object)null) && !((Object)(object)owner == (Object)null)) { _instance = owner.GetComponent() ?? owner.AddComponent(); } } internal static ForcedQuotaCompletionResult ForceCompletedQuota(int completedQuota) { if (completedQuota < 1 || completedQuota > 99) { return ForcedQuotaCompletionResult.InvalidQuota; } QuotaTakeoverDebugController instance = _instance; NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)instance == (Object)null || (Object)(object)singleton == (Object)null || !singleton.IsListening) { return ForcedQuotaCompletionResult.Unavailable; } if (!singleton.IsServer) { return ForcedQuotaCompletionResult.HostOnly; } if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)TimeOfDay.Instance == (Object)null) { return ForcedQuotaCompletionResult.NotInRound; } if (TakeoverManager.IsActive || QuotaTakeoverHandoff.QuotaJustCompleted || QuotaTakeoverHandoff.MaskManPending) { return ForcedQuotaCompletionResult.Busy; } instance.TryRegisterMessages(); int sequence = instance._nextSequence++; bool flag = IsStableOrbit(); TimeOfDay.Instance.timesFulfilledQuota = completedQuota; QuotaProgressionRegistry.OnQuotaCompleted(); instance.ApplyForcedCompletion(sequence, completedQuota, flag); instance.SendForcedCompletion(sequence, completedQuota, flag); TakeoverBootstrap.Log.LogInfo((object)($"[QuotaDebug] Forced completed quota {completedQuota}; " + (flag ? "starting takeover now." : "queued until the next stable orbit."))); if (!flag) { return ForcedQuotaCompletionResult.QueuedUntilOrbit; } return ForcedQuotaCompletionResult.StartedImmediately; } internal static bool ConsumeQueuedSkipOrbitDelay() { if ((Object)(object)_instance == (Object)null || !_instance._takeoverQueuedUntilOrbit) { return false; } _instance._takeoverQueuedUntilOrbit = false; _instance._queuedCompletedQuota = 0; return true; } internal static void ResetPending() { QuotaUnlockAnnouncement.ClearDebugReplay(); if (!((Object)(object)_instance == (Object)null)) { bool takeoverQueuedUntilOrbit = _instance._takeoverQueuedUntilOrbit; _instance._takeoverQueuedUntilOrbit = false; _instance._queuedCompletedQuota = 0; if (takeoverQueuedUntilOrbit) { QuotaTakeoverHandoff.QuotaJustCompleted = false; QuotaTakeoverHandoff.MaskManPending = false; } } } private static bool IsStableOrbit() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && instance.inShipPhase && !instance.shipHasLanded && !instance.travellingToNewLevel && !instance.beganLoadingNewLevel) { if (!((Object)(object)RoundManager.Instance == (Object)null)) { return !RoundManager.Instance.dungeonIsGenerating; } return true; } return false; } private void Awake() { if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { Object.Destroy((Object)(object)this); return; } _instance = this; TryRegisterMessages(); } private void Update() { TryRegisterMessages(); NetworkManager singleton = NetworkManager.Singleton; if (_takeoverQueuedUntilOrbit && _queuedCompletedQuota > 0 && singleton != null && singleton.IsServer && !(Time.realtimeSinceStartup < _nextQueuedBroadcastAt)) { _nextQueuedBroadcastAt = Time.realtimeSinceStartup + 2f; SendForcedCompletion(_lastAppliedSequence, _queuedCompletedQuota, startImmediately: false); } } private void OnDestroy() { UnregisterMessages(); if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } private void TryRegisterMessages() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsListening && singleton.CustomMessagingManager != null && singleton != _registeredNetworkManager) { UnregisterMessages(); _nextSequence = 1; _lastAppliedSequence = -1; _takeoverQueuedUntilOrbit = false; _queuedCompletedQuota = 0; singleton.CustomMessagingManager.RegisterNamedMessageHandler("Y4NGZCompany.ForceQuotaCompletion.v1", new HandleNamedMessageDelegate(OnForcedCompletionMessage)); _registeredNetworkManager = singleton; } } private void UnregisterMessages() { NetworkManager registeredNetworkManager = _registeredNetworkManager; if (((registeredNetworkManager != null) ? registeredNetworkManager.CustomMessagingManager : null) != null) { try { _registeredNetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler("Y4NGZCompany.ForceQuotaCompletion.v1"); } catch { } } _registeredNetworkManager = null; } private unsafe void SendForcedCompletion(int sequence, int completedQuota, bool startImmediately) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (singleton == null || !singleton.IsServer || singleton.CustomMessagingManager == null) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(9, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe(ref sequence, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe(ref completedQuota, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe(ref startImmediately, default(ForPrimitives)); singleton.CustomMessagingManager.SendNamedMessageToAll("Y4NGZCompany.ForceQuotaCompletion.v1", val, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private void OnForcedCompletionMessage(ulong senderClientId, FastBufferReader reader) { //IL_0017: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0041: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)NetworkManager.Singleton == (Object)null) && senderClientId == 0L) { int sequence = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref sequence, default(ForPrimitives)); int completedQuota = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref completedQuota, default(ForPrimitives)); bool startImmediately = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref startImmediately, default(ForPrimitives)); ApplyForcedCompletion(sequence, completedQuota, startImmediately); } } private void ApplyForcedCompletion(int sequence, int completedQuota, bool startImmediately) { if (sequence > _lastAppliedSequence && completedQuota >= 1 && completedQuota <= 99) { _lastAppliedSequence = sequence; if ((Object)(object)TimeOfDay.Instance != (Object)null) { TimeOfDay.Instance.timesFulfilledQuota = completedQuota; } QuotaTakeoverHandoff.QuotaJustCompleted = false; QuotaTakeoverHandoff.MaskManPending = false; QuotaUnlockAnnouncement.PrepareDebugReplay(completedQuota); if (completedQuota == 3) { QuotaTakeoverHandoff.MaskManPending = true; } else { QuotaTakeoverHandoff.QuotaJustCompleted = true; } _takeoverQueuedUntilOrbit = !startImmediately; _queuedCompletedQuota = ((!startImmediately) ? completedQuota : 0); _nextQueuedBroadcastAt = Time.realtimeSinceStartup + 2f; if (startImmediately && !Patches.TryStartPendingTakeover(skipOrbitDelay: true, debugForced: true)) { QuotaUnlockAnnouncement.ClearDebugReplay(); } } } } internal sealed class QuotaUnlockAnnouncement : MonoBehaviour { private struct SlotBinding { internal MeshRenderer Renderer; internal int SlotIndex; internal Material SavedMaterial; internal Material RuntimeMaterial; } private const string MessageName = "Y4NGZCompany.QuotaUnlockAnnouncement.v1"; private const string MonitorWallPath = "Environment/HangarShip/ShipModels2b/MonitorWall/Cube.001"; private const string StingClipFileName = "digital_text_sequence_terminal_01.wav"; private const int RenderWidth = 768; private const int RenderHeight = 432; private const int MaxVisibleLines = 10; private const float RevealDuration = 1f; private const float PendingPayloadTimeout = 30f; private const float QueuedRetryInterval = 2f; private const float QueuedGiveUpSeconds = 900f; private CustomMessagingManager _registeredMessagingManager; private bool _hasPending; private int _pendingQuota; private string[] _pendingLines = Array.Empty(); private float _pendingReceivedAt; private bool _hasQueued; private int _queuedQuota; private string[] _queuedLines = Array.Empty(); private bool _queuedDebugReplay; private float _queuedAt; private float _nextQueuedRetryAt; private bool _queuedBroadcastPlayed; private const string LastAnnouncedQuotaSaveKey = "Y4NGZ_UnlockAnnouncementLastQuota"; private int _lastSentQuota = -1; private string _lastSentQuotaSaveName; private int _lastShownQuota = -1; private int _debugReplayQuota = -1; private GameObject _rigRoot; private Camera _rigCamera; private RenderTexture _renderTexture; private Canvas _canvas; private TextMeshProUGUI _headerText; private TextMeshProUGUI _bodyText; private readonly List _bindings = new List(); private GeneralImprovementsMonitorLease _generalImprovementsMonitorLease; private readonly List<(ManualCameraRenderer Renderer, bool WasEnabled)> _suspendedMonitorRenderers = new List<(ManualCameraRenderer, bool)>(); private Coroutine _showCoroutine; private Coroutine _broadcastCoroutine; private bool _monitorScopeClaimed; private bool _presentationScopeClaimed; private Behaviour _suspendedLevelDescription; private bool _levelDescriptionWasEnabled; private AudioSource _audioSource; private AudioClip _tickClip; private AudioClip _ceremonyTickClip; private Texture2D _crtWhite; private Texture2D _crtBlack; private Texture2D _crtNoise; private AudioClip _crtStaticClip; internal static QuotaUnlockAnnouncement Instance { get; private set; } internal static void EnsureInstance() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Instance != (Object)null)) { new GameObject("Y4NGZ_QuotaUnlockAnnouncement").AddComponent(); } } internal static void PrepareDebugReplay(int completedQuota) { if (completedQuota > 0) { EnsureInstance(); if ((Object)(object)Instance != (Object)null) { Instance._debugReplayQuota = completedQuota; } } } internal static void ClearDebugReplay() { if ((Object)(object)Instance != (Object)null) { Instance._debugReplayQuota = -1; } } internal static void AbortForExternalTakeover() { QuotaUnlockAnnouncement instance = Instance; if (!((Object)(object)instance == (Object)null) && (instance._showCoroutine != null || instance._broadcastCoroutine != null || instance._generalImprovementsMonitorLease != null || instance._bindings.Count != 0 || instance._monitorScopeClaimed || instance._presentationScopeClaimed)) { instance.AbortAndRestore("full monitor takeover started"); } } private bool ConsumeDebugReplay(int quota) { if (_debugReplayQuota != quota) { return false; } _debugReplayQuota = -1; return true; } private void Awake() { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); TakeoverManager.OnTakeoverEnded += OnTakeoverEnded; ((MonoBehaviour)this).StartCoroutine(LoadTickAudio()); } private void OnDestroy() { if ((Object)(object)Instance != (Object)(object)this) { return; } TakeoverManager.OnTakeoverEnded -= OnTakeoverEnded; AbortAndRestore("OnDestroy"); if (_registeredMessagingManager != null) { try { _registeredMessagingManager.UnregisterNamedMessageHandler("Y4NGZCompany.QuotaUnlockAnnouncement.v1"); } catch { } } DestroyRig(); Instance = null; } private void Update() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = (((Object)(object)singleton != (Object)null && singleton.IsListening) ? singleton.CustomMessagingManager : null); if (val != null && val != _registeredMessagingManager) { if (_registeredMessagingManager != null) { try { _registeredMessagingManager.UnregisterNamedMessageHandler("Y4NGZCompany.QuotaUnlockAnnouncement.v1"); } catch { } } val.RegisterNamedMessageHandler("Y4NGZCompany.QuotaUnlockAnnouncement.v1", new HandleNamedMessageDelegate(OnAnnouncementMessage)); _registeredMessagingManager = val; } if (_hasPending && Time.realtimeSinceStartup - _pendingReceivedAt > 30f) { _hasPending = false; _pendingLines = Array.Empty(); } if ((_showCoroutine != null || _broadcastCoroutine != null) && TakeoverManager.IsActive) { AbortAndRestore("takeover started during announcement"); } else if (_showCoroutine != null) { ReassertBindings(); } TickQueuedAnnouncement(); } private void QueueAnnouncement(int quota, string[] lines, bool debugReplay, string reason) { if (lines != null && lines.Length != 0 && (!_hasQueued || _queuedQuota != quota)) { _hasQueued = true; _queuedQuota = quota; _queuedLines = lines; _queuedDebugReplay = debugReplay; _queuedAt = Time.realtimeSinceStartup; _nextQueuedRetryAt = Time.realtimeSinceStartup + 2f; ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogInfo((object)($"[QuotaUnlockAnnouncement] Queued quota {quota} announcement ({reason}); " + $"retrying every {2f:0.#}s for up to {900f:0}s.")); } } } private void ClearQueuedAnnouncement() { _hasQueued = false; _queuedQuota = 0; _queuedLines = Array.Empty(); _queuedDebugReplay = false; _queuedBroadcastPlayed = false; } private void TickQueuedAnnouncement() { if (!_hasQueued || Time.realtimeSinceStartup < _nextQueuedRetryAt) { return; } _nextQueuedRetryAt = Time.realtimeSinceStartup + 2f; if (Time.realtimeSinceStartup - _queuedAt > 900f) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)($"[QuotaUnlockAnnouncement] Giving up on the queued quota {_queuedQuota} announcement " + $"after {900f:0}s: the large monitor never became available.")); } ClearQueuedAnnouncement(); } else if (_showCoroutine == null && _broadcastCoroutine == null && !TakeoverManager.IsActive && (!ShipSystemsServices.IsPrimaryMonitorTakeoverActive || GeneralImprovementsMonitors.BetterMonitorsActive)) { int queuedQuota = _queuedQuota; string[] queuedLines = _queuedLines; bool queuedDebugReplay = _queuedDebugReplay; ClearQueuedAnnouncement(); ManualLogSource log2 = TakeoverBootstrap.Log; if (log2 != null) { log2.LogInfo((object)$"[QuotaUnlockAnnouncement] Dequeued quota {queuedQuota} announcement; the large monitor is free."); } TryShow(queuedQuota, queuedLines, queuedDebugReplay); } } private void OnTakeoverEnded() { try { if (!QuotaProgressionRegistry.EnableUnlockAnnouncement) { return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && singleton.IsServer) { int num = (((Object)(object)TimeOfDay.Instance != (Object)null) ? Math.Max(0, TimeOfDay.Instance.timesFulfilledQuota) : 0); bool debugReplay = ConsumeDebugReplay(num); if (!QuotaUnlockAnnouncementPolicy.ShouldDispatch(num, GetLastAnnouncedQuota(), debugReplay)) { return; } string[] array = BuildUnlockLines(num); if (array.Length != 0) { if (QuotaUnlockAnnouncementPolicy.ShouldPersist(debugReplay)) { SetLastAnnouncedQuota(num); } SendAnnouncement(num, array); TryShow(num, array, debugReplay); } } else if (_hasPending) { _hasPending = false; string[] pendingLines = _pendingLines; _pendingLines = Array.Empty(); bool debugReplay2 = ConsumeDebugReplay(_pendingQuota); TryShow(_pendingQuota, pendingLines, debugReplay2); } } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] OnTakeoverEnded failed: " + ex.Message)); } } } private unsafe void SendAnnouncement(int quota, string[] lines) { //IL_0050: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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) NetworkManager singleton = NetworkManager.Singleton; CustomMessagingManager val = ((singleton != null) ? singleton.CustomMessagingManager : null); if (val == null) { return; } string text = string.Join("\n", lines); if (text.Length > 1000) { text = text.Substring(0, 1000); } FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(6144, (Allocator)2, -1); try { ((FastBufferWriter)(ref val2)).WriteValueSafe(ref quota, default(ForPrimitives)); FixedString4096Bytes val3 = FixedString4096Bytes.op_Implicit(text); ((FastBufferWriter)(ref val2)).WriteValueSafe(ref val3, default(ForFixedStrings)); val.SendNamedMessageToAll("Y4NGZCompany.QuotaUnlockAnnouncement.v1", val2, (NetworkDelivery)3); } finally { ((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose(); } } private int GetLastAnnouncedQuota() { string text = GameNetworkManager.Instance?.currentSaveFileName; if (string.IsNullOrEmpty(text)) { return _lastSentQuota; } if (_lastSentQuotaSaveName != text) { _lastSentQuotaSaveName = text; try { _lastSentQuota = ES3.Load("Y4NGZ_UnlockAnnouncementLastQuota", text, -1); } catch (Exception ex) { _lastSentQuota = -1; ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] Failed loading last announced quota: " + ex.Message)); } } } return _lastSentQuota; } private void SetLastAnnouncedQuota(int quota) { _lastSentQuota = quota; string text = GameNetworkManager.Instance?.currentSaveFileName; if (string.IsNullOrEmpty(text)) { return; } _lastSentQuotaSaveName = text; try { ES3.Save("Y4NGZ_UnlockAnnouncementLastQuota", quota, text); } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] Failed persisting last announced quota: " + ex.Message)); } } } private unsafe void OnAnnouncementMessage(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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (sender != 0L) { return; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe(ref num, default(ForPrimitives)); FixedString4096Bytes val = default(FixedString4096Bytes); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val, default(ForFixedStrings)); NetworkManager singleton = NetworkManager.Singleton; if ((singleton != null && singleton.IsServer) || !QuotaProgressionRegistry.EnableUnlockAnnouncement) { return; } string[] array = ((object)(*(FixedString4096Bytes*)(&val))/*cast due to .constrained prefix*/).ToString().Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length != 0) { if (TakeoverManager.IsActive) { _hasPending = true; _pendingQuota = num; _pendingLines = array; _pendingReceivedAt = Time.realtimeSinceStartup; } else { TryShow(num, array, ConsumeDebugReplay(num)); } } } private static string[] BuildUnlockLines(int quota) { List list = new List(); QuotaProgressionRegistry.QuotaMilestoneSummary milestoneSummary = QuotaProgressionRegistry.GetMilestoneSummary(quota); List list2 = new List(); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (milestoneSummary != null && milestoneSummary.Enabled) { foreach (string contract in milestoneSummary.Contracts) { list.Add("CONTRACT · " + DisplayName(contract)); } foreach (string shipUpgrade in milestoneSummary.ShipUpgrades) { list.Add("SHIP UPGRADE · " + DisplayName(shipUpgrade)); } foreach (string constellation in milestoneSummary.Constellations) { if (hashSet.Add(constellation)) { list2.Add(constellation); } } } try { HashSet hashSet2 = new HashSet(ExternalQuotaUnlocks.GetUnlockedAtQuota("constellation", quota - 1)); foreach (string unlockedAtQuotum in ExternalQuotaUnlocks.GetUnlockedAtQuota("constellation", quota)) { if (!hashSet2.Contains(unlockedAtQuotum) && hashSet.Add(unlockedAtQuotum)) { list2.Add(unlockedAtQuotum); } } } catch { } foreach (string item in list2) { list.Add("CONSTELLATION · " + DisplayName(item)); } if (milestoneSummary != null && milestoneSummary.Enabled) { foreach (string suit in milestoneSummary.Suits) { list.Add("SUIT · " + DisplayName(suit)); } foreach (string storeItem in milestoneSummary.StoreItems) { list.Add("STORE · " + DisplayName(storeItem)); } if (milestoneSummary.TokensPerPlayer > 0) { list.Add($"+{milestoneSummary.TokensPerPlayer} TOKENS PER PLAYER"); } if (milestoneSummary.GroupCredits > 0) { list.Add($"+{milestoneSummary.GroupCredits} CREDITS"); } if (milestoneSummary.ShipFuel > 0f && ShipSystemsServices.IsFuelSystemEnabled) { list.Add($"+{milestoneSummary.ShipFuel:0} FUEL"); } } return list.ToArray(); } private static string DisplayName(string raw) { raw = (raw ?? string.Empty).Trim(); List list = new List(raw.Length + 8); for (int i = 0; i < raw.Length; i++) { char c = raw[i]; if (i > 0 && char.IsUpper(c) && char.IsLower(raw[i - 1])) { list.Add(' '); } list.Add(c); } return new string(list.ToArray()).ToUpperInvariant(); } private bool ClaimPresentationScope(int quota) { try { ShipSystemsServices.SetQuotaPresentationActive(true, quota); _presentationScopeClaimed = true; return true; } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] Ship-wide presentation scope failed: " + ex.Message)); } return false; } } private IEnumerator BroadcastOnlySequence(int quota) { if (!ClaimPresentationScope(quota)) { _broadcastCoroutine = null; yield break; } float seconds = Mathf.Clamp(QuotaProgressionRegistry.UnlockAnnouncementSeconds, 2f, 15f); ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogInfo((object)($"[QuotaUnlockAnnouncement] Large monitor unavailable; running the ship-wide quota {quota} " + $"broadcast alone for {seconds:0.#}s.")); } float started = Time.realtimeSinceStartup; while (Time.realtimeSinceStartup - started < seconds && !TakeoverManager.IsActive) { yield return null; } RestoreAll(); _broadcastCoroutine = null; } private void TryShow(int quota, string[] lines, bool debugReplay = false) { if (lines == null || lines.Length == 0) { return; } if (QuotaUnlockAnnouncementPolicy.ShouldSuppressPresentation(quota, _lastShownQuota, debugReplay)) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogInfo((object)($"[QuotaUnlockAnnouncement] Skipping quota {quota} announcement: policy suppressed " + $"(lastShown={_lastShownQuota}, debugReplay={debugReplay}).")); } } else if (_showCoroutine != null || _broadcastCoroutine != null) { QueueAnnouncement(quota, lines, debugReplay, "another quota presentation is on screen"); } else if (TakeoverManager.IsActive) { QueueAnnouncement(quota, lines, debugReplay, "a monitor takeover is running"); } else { if (_hasQueued && _queuedQuota == quota) { ClearQueuedAnnouncement(); } _showCoroutine = ((MonoBehaviour)this).StartCoroutine(ShowSequence(quota, lines, debugReplay)); } } private IEnumerator ShowSequence(int quota, string[] lines, bool debugReplay = false) { bool flag = false; bool primaryScopeHeld = false; try { EnsureRig(); flag = BindMonitors(out primaryScopeHeld); } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] Setup failed: " + ex.Message)); } } if (!flag) { RestoreAll(); _showCoroutine = null; if (primaryScopeHeld) { bool num = _hasQueued && _queuedQuota == quota && _queuedBroadcastPlayed; QueueAnnouncement(quota, lines, debugReplay, "primary monitor scope held by another system"); if (!num) { _queuedBroadcastPlayed = true; _broadcastCoroutine = ((MonoBehaviour)this).StartCoroutine(BroadcastOnlySequence(quota)); } } yield break; } _lastShownQuota = quota; ClaimPresentationScope(quota); if (lines.Length > 10) { int num2 = lines.Length - 9; lines = lines.Take(9).Concat(new string[1] { $"+{num2} MORE" }).ToArray(); } float totalSeconds = Mathf.Clamp(QuotaProgressionRegistry.UnlockAnnouncementSeconds, 2f, 15f); bool ceremony = QuotaProgressionRegistry.RewardsCeremonyEnabled; float started = Time.realtimeSinceStartup; try { ((TMP_Text)_headerText).text = $"QUOTA {quota} · NEW AUTHORIZATIONS"; ((TMP_Text)_bodyText).text = string.Empty; RenderRig(); PlayAppearSting(); } catch (Exception ex2) { ManualLogSource log2 = TakeoverBootstrap.Log; if (log2 != null) { log2.LogWarning((object)("[QuotaUnlockAnnouncement] Header render failed: " + ex2.Message)); } } if (!ceremony) { float lineInterval = 1f / (float)Mathf.Max(1, lines.Length); for (int i = 0; i < lines.Length; i++) { yield return (object)new WaitForSecondsRealtime(lineInterval); try { ((TMP_Text)_bodyText).text = string.Join("\n", lines.Take(i + 1)); RenderRig(); PlayLineTick(); } catch (Exception ex3) { ManualLogSource log3 = TakeoverBootstrap.Log; if (log3 != null) { log3.LogWarning((object)("[QuotaUnlockAnnouncement] Line render failed: " + ex3.Message)); } } } } else { MonitorRowTypewriterStep[] steps = MonitorRowCeremonyMath.BuildTypewriterSchedule(lines, QuotaProgressionRegistry.CeremonyTypewriterSecondsPerChar, 1f / 15f, 20f); StringBuilder body = new StringBuilder(256); int i = 0; for (int j = 0; j < steps.Length; j++) { if (TakeoverManager.IsActive) { break; } MonitorRowTypewriterStep step = steps[j]; string line = lines[((MonitorRowTypewriterStep)(ref step)).LineIndex]; try { body.Length = i; body.Append(line, 0, ((MonitorRowTypewriterStep)(ref step)).VisibleChars); ((TMP_Text)_bodyText).text = body.ToString(); RenderRig(); PlayCeremonyTypeTick(); } catch (Exception ex4) { ManualLogSource log4 = TakeoverBootstrap.Log; if (log4 != null) { log4.LogWarning((object)("[QuotaUnlockAnnouncement] Ceremony render failed: " + ex4.Message)); } } if (((MonitorRowTypewriterStep)(ref step)).DelaySeconds > 0f) { yield return (object)new WaitForSecondsRealtime(((MonitorRowTypewriterStep)(ref step)).DelaySeconds); } if (((MonitorRowTypewriterStep)(ref step)).LineCompleted) { body.Length = i; body.Append(line).Append('\n'); i = body.Length; if (QuotaProgressionRegistry.CeremonyLineConfirmSting) { PlayLineTick(); } if (((MonitorRowTypewriterStep)(ref step)).LineIndex < lines.Length - 1) { yield return (object)new WaitForSecondsRealtime(0.15f); } } } started = Time.realtimeSinceStartup; } while (Time.realtimeSinceStartup - started < totalSeconds && !TakeoverManager.IsActive) { yield return null; } if (ceremony && QuotaProgressionRegistry.CeremonyRestoreStatic && !TakeoverManager.IsActive) { yield return CrtRestoreBurst(); } RestoreAll(); _showCoroutine = null; } private IEnumerator CrtRestoreBurst() { bool flag = false; try { if ((Object)(object)_crtWhite == (Object)null) { _crtWhite = CRTStatic.CreateSolidTexture(Color.white); } if ((Object)(object)_crtBlack == (Object)null) { _crtBlack = CRTStatic.CreateSolidTexture(Color.black); } if ((Object)(object)_crtNoise == (Object)null) { _crtNoise = CRTStatic.CreateNoiseTexture(256, 256); } if ((Object)(object)_crtStaticClip == (Object)null) { _crtStaticClip = CRTStatic.CreateBurstClip(); } flag = (Object)(object)_renderTexture != (Object)null && _renderTexture.IsCreated(); if (flag && (Object)(object)_audioSource != (Object)null) { _audioSource.PlayOneShot(_crtStaticClip, 0.5f); } } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] CRT burst setup failed: " + ex.Message)); } } if (!flag || !TryBlitCrtFrame(_crtWhite)) { yield break; } yield return (object)new WaitForSecondsRealtime(0.05f); if (!TakeoverManager.IsActive && TryBlitCrtFrame(_crtNoise)) { yield return (object)new WaitForSecondsRealtime(0.2f); if (!TakeoverManager.IsActive && TryBlitCrtFrame(_crtBlack)) { yield return (object)new WaitForSecondsRealtime(0.12f); } } } private bool TryBlitCrtFrame(Texture2D frame) { if ((Object)(object)frame == (Object)null || (Object)(object)_renderTexture == (Object)null || !_renderTexture.IsCreated()) { return false; } RenderTexture active = RenderTexture.active; try { Graphics.Blit((Texture)(object)frame, _renderTexture); return true; } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] CRT frame blit failed: " + ex.Message)); } return false; } finally { RenderTexture.active = active; } } private void ReleaseCrtFrames() { if ((Object)(object)_crtWhite != (Object)null) { Object.Destroy((Object)(object)_crtWhite); _crtWhite = null; } if ((Object)(object)_crtBlack != (Object)null) { Object.Destroy((Object)(object)_crtBlack); _crtBlack = null; } if ((Object)(object)_crtNoise != (Object)null) { Object.Destroy((Object)(object)_crtNoise); _crtNoise = null; } } private void EnsureRig() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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_00f2: 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_0105: Expected O, but got Unknown //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Expected O, but got Unknown //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_rigRoot != (Object)null && (Object)(object)_rigCamera != (Object)null && (Object)(object)_canvas != (Object)null && (Object)(object)_headerText != (Object)null && (Object)(object)_bodyText != (Object)null && (Object)(object)_renderTexture != (Object)null && _renderTexture.IsCreated()) { return; } DestroyRig(); _rigRoot = new GameObject("Y4NGZ_UnlockAnnouncementRig"); Object.DontDestroyOnLoad((Object)(object)_rigRoot); _rigRoot.transform.position = new Vector3(OffscreenRigParking.SlotX((OffscreenRigSlot)2), -10000f, 0f); if (!OffscreenRigParking.Fits(384f, 216f)) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)"[QuotaUnlockAnnouncement] Rig exceeds its parking slot; it may be captured by a neighbouring rig's camera."); } } _renderTexture = new RenderTexture(768, 432, 0, (RenderTextureFormat)0) { name = "Y4NGZ_UnlockAnnouncementRT", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, antiAliasing = 1 }; if (!_renderTexture.Create()) { DestroyRig(); throw new InvalidOperationException("Quota announcement RenderTexture.Create() failed."); } GameObject val = new GameObject("AnnouncementCamera"); val.transform.SetParent(_rigRoot.transform, false); val.transform.localPosition = new Vector3(0f, 0f, -10f); _rigCamera = val.AddComponent(); _rigCamera.clearFlags = (CameraClearFlags)2; _rigCamera.backgroundColor = MonitorPalette.MonitorBackground; _rigCamera.orthographic = true; _rigCamera.orthographicSize = 216f; _rigCamera.aspect = 1.7777778f; _rigCamera.nearClipPlane = 0.1f; _rigCamera.farClipPlane = 30f; _rigCamera.cullingMask = 32; _rigCamera.targetTexture = _renderTexture; ((Behaviour)_rigCamera).enabled = false; HDAdditionalCameraData obj = val.AddComponent(); obj.clearColorMode = (ClearColorMode)1; obj.backgroundColorHDR = _rigCamera.backgroundColor; obj.volumeLayerMask = LayerMask.op_Implicit(0); obj.customRenderingSettings = true; ref FrameSettings renderingPathCustomFrameSettings = ref obj.renderingPathCustomFrameSettings; ref FrameSettingsOverrideMask renderingPathCustomFrameSettingsOverrideMask = ref obj.renderingPathCustomFrameSettingsOverrideMask; DisableFrameSetting(ref renderingPathCustomFrameSettings, ref renderingPathCustomFrameSettingsOverrideMask, (FrameSettingsField)15); DisableFrameSetting(ref renderingPathCustomFrameSettings, ref renderingPathCustomFrameSettingsOverrideMask, (FrameSettingsField)39); DisableFrameSetting(ref renderingPathCustomFrameSettings, ref renderingPathCustomFrameSettingsOverrideMask, (FrameSettingsField)17); DisableFrameSetting(ref renderingPathCustomFrameSettings, ref renderingPathCustomFrameSettingsOverrideMask, (FrameSettingsField)32); DisableFrameSetting(ref renderingPathCustomFrameSettings, ref renderingPathCustomFrameSettingsOverrideMask, (FrameSettingsField)28); GameObject val2 = new GameObject("AnnouncementCanvas", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent(_rigRoot.transform, false); _canvas = val2.AddComponent(); _canvas.renderMode = (RenderMode)2; _canvas.worldCamera = _rigCamera; RectTransform component = val2.GetComponent(); component.sizeDelta = new Vector2(768f, 432f); Image obj2 = val2.AddComponent(); ((Graphic)obj2).color = MonitorPalette.MonitorBackground; ((Graphic)obj2).raycastTarget = false; _headerText = CreateText((Transform)(object)component, "Header", 40f, (TextAlignmentOptions)514, MonitorPalette.MonitorLabel); ((TMP_Text)_headerText).fontStyle = (FontStyles)1; SetOffsets(((TMP_Text)_headerText).rectTransform, new Vector2(24f, 348f), new Vector2(-24f, -20f)); _bodyText = CreateText((Transform)(object)component, "Body", 30f, (TextAlignmentOptions)258, MonitorPalette.UiAccent); ((TMP_Text)_bodyText).lineSpacing = 8f; SetOffsets(((TMP_Text)_bodyText).rectTransform, new Vector2(48f, 20f), new Vector2(-48f, -96f)); SetLayerRecursive(_rigRoot, 5); ((Behaviour)_canvas).enabled = false; _audioSource = _rigRoot.AddComponent(); _audioSource.playOnAwake = false; _audioSource.spatialBlend = 0f; } private void DestroyRig() { ReleaseCrtFrames(); if ((Object)(object)_rigCamera != (Object)null) { ((Behaviour)_rigCamera).enabled = false; _rigCamera.targetTexture = null; } if ((Object)(object)_renderTexture != (Object)null) { if (_renderTexture.IsCreated()) { _renderTexture.Release(); } Object.Destroy((Object)(object)_renderTexture); } if ((Object)(object)_rigRoot != (Object)null) { Object.Destroy((Object)(object)_rigRoot); } _rigRoot = null; _rigCamera = null; _renderTexture = null; _canvas = null; _headerText = null; _bodyText = null; _audioSource = null; } private static TextMeshProUGUI CreateText(Transform parent, string name, float size, TextAlignmentOptions alignment, Color color) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.pivot = new Vector2(0.5f, 0.5f); TextMeshProUGUI obj = val.AddComponent(); ((TMP_Text)obj).fontSize = size; ((TMP_Text)obj).alignment = alignment; ((TMP_Text)obj).enableWordWrapping = false; ((TMP_Text)obj).richText = false; ((Graphic)obj).color = color; ((Graphic)obj).raycastTarget = false; AssignVanillaFont(obj); return obj; } private static void AssignVanillaFont(TextMeshProUGUI text) { TextMeshProUGUI val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.screenLevelDescription : null); if ((Object)(object)val != (Object)null && (Object)(object)((TMP_Text)val).font != (Object)null) { ((TMP_Text)text).font = ((TMP_Text)val).font; if ((Object)(object)((TMP_Text)val).fontSharedMaterial != (Object)null) { ((TMP_Text)text).fontSharedMaterial = ((TMP_Text)val).fontSharedMaterial; } } else if ((Object)(object)HUDManager.Instance != (Object)null && (Object)(object)HUDManager.Instance.clockNumber != (Object)null && (Object)(object)((TMP_Text)HUDManager.Instance.clockNumber).font != (Object)null) { ((TMP_Text)text).font = ((TMP_Text)HUDManager.Instance.clockNumber).font; } } private static void SetOffsets(RectTransform rt, Vector2 offsetMin, Vector2 offsetMax) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) rt.offsetMin = offsetMin; rt.offsetMax = offsetMax; } private static void SetLayerRecursive(GameObject go, int layer) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) go.layer = layer; foreach (Transform item in go.transform) { SetLayerRecursive(((Component)item).gameObject, layer); } } private static void DisableFrameSetting(ref FrameSettings frameSettings, ref FrameSettingsOverrideMask overrideMask, FrameSettingsField field) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected I4, but got Unknown ((FrameSettings)(ref frameSettings)).SetEnabled(field, false); ((BitArray128)(ref overrideMask.mask))[(uint)(int)field] = true; } private void RenderRig() { if ((Object)(object)_rigCamera == (Object)null || (Object)(object)_canvas == (Object)null || (Object)(object)_headerText == (Object)null || (Object)(object)_bodyText == (Object)null || (Object)(object)_renderTexture == (Object)null || !_renderTexture.IsCreated()) { throw new InvalidOperationException("Quota announcement render rig is unavailable."); } RenderTexture active = RenderTexture.active; try { ((Behaviour)_canvas).enabled = true; Canvas.ForceUpdateCanvases(); ((TMP_Text)_headerText).ForceMeshUpdate(false, false); ((TMP_Text)_bodyText).ForceMeshUpdate(false, false); ReassertBindings(); _rigCamera.Render(); } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] Offscreen render failed: " + ex.Message)); } throw; } finally { ((Behaviour)_canvas).enabled = false; RenderTexture.active = active; } } private bool BindMonitors(out bool primaryScopeHeld) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown primaryScopeHeld = false; bool flag = QuotaProgressionRegistry.AnnounceOnBothMonitors || (QuotaProgressionRegistry.RewardsCeremonyEnabled && QuotaProgressionRegistry.CeremonyMirrorBothMonitors); if (ShipSystemsServices.IsExternalMonitorTakeoverActive) { primaryScopeHeld = true; ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogInfo((object)"[QuotaUnlockAnnouncement] Full monitor takeover active; deferring the large-monitor announcement."); } return false; } if (!TakeoverMonitorOwnership.ClaimAnnouncementScreens(flag ? "quota unlock announcement (mirrored)" : "quota unlock announcement")) { primaryScopeHeld = true; ManualLogSource log2 = TakeoverBootstrap.Log; if (log2 != null) { log2.LogInfo((object)"[QuotaUnlockAnnouncement] Announcement screens are owned by a stronger claimant; deferring the large-monitor announcement."); } return false; } if (GeneralImprovementsMonitors.BetterMonitorsActive) { GeneralImprovementsMonitorLease generalImprovementsMonitorLease = _generalImprovementsMonitorLease; if (generalImprovementsMonitorLease != null) { generalImprovementsMonitorLease.Release(); } _generalImprovementsMonitorLease = new GeneralImprovementsMonitorLease((SurfaceSet)((!flag) ? 1 : 2)); string arg = default(string); bool num = _generalImprovementsMonitorLease.Acquire((Texture)(object)_renderTexture, ref arg); if (!num) { primaryScopeHeld = true; } ManualLogSource log3 = TakeoverBootstrap.Log; if (log3 != null) { log3.LogInfo((object)$"[QuotaUnlockAnnouncement] {arg}; mirror={flag}."); return num; } return num; } MeshRenderer val = FindMonitorWallRenderer(); if ((Object)(object)val == (Object)null) { ManualLogSource log4 = TakeoverBootstrap.Log; if (log4 != null) { log4.LogInfo((object)"[QuotaUnlockAnnouncement] MonitorWall Cube.001 not found; skipping announcement."); } return false; } if (ShipSystemsServices.IsPrimaryMonitorTakeoverActive) { primaryScopeHeld = true; ManualLogSource log5 = TakeoverBootstrap.Log; if (log5 != null) { log5.LogInfo((object)"[QuotaUnlockAnnouncement] Primary monitor scope already claimed (CCTV?); deferring the large-monitor announcement."); } return false; } if (flag) { OpenBodyCamsCompat.SuspendForAnnouncement(); } int num2 = ResolveLeftMonitorMaterialIndex(val); int num3 = (flag ? ResolveSecondaryScreenIndex(val, num2) : (-1)); bool flag2 = false; ShipSystemsServices.SetPrimaryMonitorTakeoverActive(true); _monitorScopeClaimed = true; SuspendMonitorProducers(val, num2, num3); if (num2 >= 0) { flag2 |= BindSlot(val, num2); } bool flag3 = false; if (num3 >= 0 && num3 != num2) { flag3 = BindSlot(val, num3); } flag2 = flag2 || flag3; if (!flag3) { OpenBodyCamsCompat.RestoreForAnnouncement(); } if (!flag2) { ManualLogSource log6 = TakeoverBootstrap.Log; if (log6 != null) { log6.LogInfo((object)"[QuotaUnlockAnnouncement] No bindable monitor slots resolved; skipping announcement."); } return false; } return true; } private void SuspendMonitorProducers(MeshRenderer targetRenderer, int leftIndex, int rightIndex) { RestoreMonitorProducers(); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return; } try { ManualCameraRenderer mapScreen = instance.mapScreen; SuspendMonitorProducer(mapScreen, targetRenderer, leftIndex, rightIndex); ManualCameraRenderer[] array = Object.FindObjectsOfType(); foreach (ManualCameraRenderer mapScreen2 in array) { SuspendMonitorProducer(mapScreen2, targetRenderer, leftIndex, rightIndex); } } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] Monitor producer suspension failed: " + ex.Message)); } } _suspendedLevelDescription = (Behaviour)(object)instance.screenLevelDescription; if ((Object)(object)_suspendedLevelDescription != (Object)null) { _levelDescriptionWasEnabled = _suspendedLevelDescription.enabled; _suspendedLevelDescription.enabled = false; } } private void SuspendMonitorProducer(ManualCameraRenderer mapScreen, MeshRenderer targetRenderer, int leftIndex, int rightIndex) { if ((Object)(object)mapScreen == (Object)null || (Object)(object)mapScreen.mesh != (Object)(object)targetRenderer || (mapScreen.materialIndex != leftIndex && mapScreen.materialIndex != rightIndex)) { return; } for (int i = 0; i < _suspendedMonitorRenderers.Count; i++) { if ((Object)(object)_suspendedMonitorRenderers[i].Renderer == (Object)(object)mapScreen) { return; } } _suspendedMonitorRenderers.Add((mapScreen, ((Behaviour)mapScreen).enabled)); ((Behaviour)mapScreen).enabled = false; } private void RestoreMonitorProducers() { for (int i = 0; i < _suspendedMonitorRenderers.Count; i++) { try { (ManualCameraRenderer, bool) tuple = _suspendedMonitorRenderers[i]; if ((Object)(object)tuple.Item1 != (Object)null) { ((Behaviour)tuple.Item1).enabled = tuple.Item2; } } catch { } } _suspendedMonitorRenderers.Clear(); if ((Object)(object)_suspendedLevelDescription != (Object)null) { try { _suspendedLevelDescription.enabled = _levelDescriptionWasEnabled; } catch { } } _suspendedLevelDescription = null; _levelDescriptionWasEnabled = false; } private static MeshRenderer FindMonitorWallRenderer() { GameObject val = GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall/Cube.001"); MeshRenderer val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)val2 != (Object)null) { return val2; } Transform val3 = (((Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.elevatorTransform != (Object)null) ? StartOfRound.Instance.elevatorTransform.Find("ShipModels2b/MonitorWall/Cube.001") : null); if (!((Object)(object)val3 != (Object)null)) { return null; } return ((Component)val3).GetComponent(); } private static int ResolveLeftMonitorMaterialIndex(MeshRenderer renderer) { Material[] sharedMaterials; try { sharedMaterials = ((Renderer)renderer).sharedMaterials; } catch { return -1; } int num = (((Object)(object)StartOfRound.Instance?.mapScreen != (Object)null && (Object)(object)StartOfRound.Instance.mapScreen.mesh == (Object)(object)renderer) ? StartOfRound.Instance.mapScreen.materialIndex : (-1)); if (num >= 0 && num < sharedMaterials.Length) { return num; } if (sharedMaterials.Length <= 1) { return -1; } return 1; } private static int ResolveSecondaryScreenIndex(MeshRenderer renderer, int leftIndex) { Material[] sharedMaterials; try { sharedMaterials = ((Renderer)renderer).sharedMaterials; } catch { return -1; } if (sharedMaterials.Length < 3) { return -1; } int result = -1; int num = 199; for (int i = 0; i < sharedMaterials.Length; i++) { if (i == leftIndex) { continue; } Material val = sharedMaterials[i]; if ((Object)(object)val == (Object)null) { continue; } string text = ((Object)val).name ?? string.Empty; if (text.IndexOf("BodyCam", StringComparison.OrdinalIgnoreCase) < 0) { int num2 = 0; string obj2 = (((Object)(object)val.shader != (Object)null) ? ((Object)val.shader).name : string.Empty); if (obj2.IndexOf("Unlit", StringComparison.OrdinalIgnoreCase) >= 0) { num2 += 250; } if (text.IndexOf("ShipScreen", StringComparison.OrdinalIgnoreCase) >= 0) { num2 += 180; } if (text.IndexOf("Map", StringComparison.OrdinalIgnoreCase) >= 0) { num2 -= 120; } if (val.HasProperty("_UnlitColorMap")) { num2 += 120; } if (val.mainTexture is RenderTexture) { num2 += 70; } if (obj2.IndexOf("HDRP/Lit", StringComparison.OrdinalIgnoreCase) >= 0) { num2 -= 180; } if (num2 > num) { num = num2; result = i; } } } return result; } private bool BindSlot(MeshRenderer renderer, int slotIndex) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown Material[] sharedMaterials; try { sharedMaterials = ((Renderer)renderer).sharedMaterials; } catch { return false; } if (sharedMaterials == null || slotIndex < 0 || slotIndex >= sharedMaterials.Length) { return false; } Material val = sharedMaterials[slotIndex]; if ((Object)(object)val == (Object)null) { return false; } if ((((Object)(object)val.shader != (Object)null) ? ((Object)val.shader).name : string.Empty).IndexOf("HDRP/Lit", StringComparison.Ordinal) >= 0) { return false; } Material val2 = new Material(val) { name = ((Object)val).name + "_Y4NGZQuotaAnnouncement" }; ApplyTextureToMaterial(val2, (Texture)(object)_renderTexture); sharedMaterials[slotIndex] = val2; try { ((Renderer)renderer).sharedMaterials = sharedMaterials; } catch { Object.Destroy((Object)(object)val2); return false; } _bindings.Add(new SlotBinding { Renderer = renderer, SlotIndex = slotIndex, SavedMaterial = val, RuntimeMaterial = val2 }); ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogInfo((object)($"[QuotaUnlockAnnouncement] Bound monitor materialIndex={slotIndex}; " + $"renderTextureCreated={(Object)(object)_renderTexture != (Object)null && _renderTexture.IsCreated()}.")); } return true; } private static void ApplyTextureToMaterial(Material material, Texture texture) { //IL_007d: 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_00a4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)material == (Object)null || (Object)(object)texture == (Object)null) { return; } try { material.mainTexture = texture; } catch { } string[] array = new string[4] { "_BaseColorMap", "_UnlitColorMap", "_BaseMap", "_EmissiveColorMap" }; for (int i = 0; i < array.Length; i++) { if (material.HasProperty(array[i])) { material.SetTexture(array[i], texture); } } if (material.HasProperty("_EmissiveColor")) { material.SetColor("_EmissiveColor", Color.white * 1.5f); } if (material.HasProperty("_EmissiveColorLDR")) { material.SetColor("_EmissiveColorLDR", Color.white); } material.EnableKeyword("_EMISSION"); material.EnableKeyword("_EMISSIVE_COLOR_MAP"); } private void ReassertBindings() { GeneralImprovementsMonitorLease generalImprovementsMonitorLease = _generalImprovementsMonitorLease; if (generalImprovementsMonitorLease != null) { generalImprovementsMonitorLease.Maintain(false); } for (int i = 0; i < _bindings.Count; i++) { SlotBinding slotBinding = _bindings[i]; if ((Object)(object)slotBinding.Renderer == (Object)null || (Object)(object)slotBinding.RuntimeMaterial == (Object)null) { continue; } try { Material[] sharedMaterials = ((Renderer)slotBinding.Renderer).sharedMaterials; if (slotBinding.SlotIndex >= 0 && slotBinding.SlotIndex < sharedMaterials.Length && (Object)(object)sharedMaterials[slotBinding.SlotIndex] != (Object)(object)slotBinding.RuntimeMaterial) { sharedMaterials[slotBinding.SlotIndex] = slotBinding.RuntimeMaterial; ((Renderer)slotBinding.Renderer).sharedMaterials = sharedMaterials; } } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] Slot reassert failed: " + ex.Message)); } } } for (int j = 0; j < _suspendedMonitorRenderers.Count; j++) { ManualCameraRenderer item = _suspendedMonitorRenderers[j].Renderer; if ((Object)(object)item != (Object)null && ((Behaviour)item).enabled) { ((Behaviour)item).enabled = false; } } if ((Object)(object)_suspendedLevelDescription != (Object)null && _suspendedLevelDescription.enabled) { _suspendedLevelDescription.enabled = false; } } private void RestoreAll() { ReleaseCrtFrames(); TakeoverMonitorOwnership.ReleaseAnnouncementScreens(); GeneralImprovementsMonitorLease generalImprovementsMonitorLease = _generalImprovementsMonitorLease; if (generalImprovementsMonitorLease != null) { generalImprovementsMonitorLease.Release(); } _generalImprovementsMonitorLease = null; foreach (SlotBinding binding in _bindings) { try { if (!((Object)(object)binding.Renderer == (Object)null)) { Material[] sharedMaterials = ((Renderer)binding.Renderer).sharedMaterials; if (sharedMaterials != null && binding.SlotIndex >= 0 && binding.SlotIndex < sharedMaterials.Length && (Object)(object)sharedMaterials[binding.SlotIndex] == (Object)(object)binding.RuntimeMaterial) { sharedMaterials[binding.SlotIndex] = binding.SavedMaterial; ((Renderer)binding.Renderer).sharedMaterials = sharedMaterials; } } } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] Slot restore failed: " + ex.Message)); } } finally { if ((Object)(object)binding.RuntimeMaterial != (Object)null) { Object.Destroy((Object)(object)binding.RuntimeMaterial); } } } _bindings.Clear(); RestoreMonitorProducers(); OpenBodyCamsCompat.RestoreForAnnouncement(); if (_presentationScopeClaimed) { _presentationScopeClaimed = false; try { ShipSystemsServices.SetQuotaPresentationActive(false, 0); } catch (Exception ex2) { ManualLogSource log2 = TakeoverBootstrap.Log; if (log2 != null) { log2.LogWarning((object)("[QuotaUnlockAnnouncement] Presentation scope release failed: " + ex2.Message)); } } } if (!_monitorScopeClaimed) { return; } _monitorScopeClaimed = false; try { ShipSystemsServices.SetPrimaryMonitorTakeoverActive(false); } catch (Exception ex3) { ManualLogSource log3 = TakeoverBootstrap.Log; if (log3 != null) { log3.LogWarning((object)("[QuotaUnlockAnnouncement] Monitor scope release failed: " + ex3.Message)); } } } private void AbortAndRestore(string reason) { if (_showCoroutine != null) { try { ((MonoBehaviour)this).StopCoroutine(_showCoroutine); } catch { } _showCoroutine = null; ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogInfo((object)("[QuotaUnlockAnnouncement] Aborted (" + reason + ").")); } } if (_broadcastCoroutine != null) { try { ((MonoBehaviour)this).StopCoroutine(_broadcastCoroutine); } catch { } _broadcastCoroutine = null; ManualLogSource log2 = TakeoverBootstrap.Log; if (log2 != null) { log2.LogInfo((object)("[QuotaUnlockAnnouncement] Broadcast-only presentation aborted (" + reason + ").")); } } RestoreAll(); } private void PlayAppearSting() { HUDManager instance = HUDManager.Instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.UIAudio == (Object)null)) { AudioClip[] tipsSFX = instance.tipsSFX; if (tipsSFX != null && tipsSFX.Length != 0 && (Object)(object)tipsSFX[0] != (Object)null) { instance.UIAudio.PlayOneShot(tipsSFX[0], 0.5f); } else if ((Object)(object)instance.finishAddingToTotalSFX != (Object)null) { instance.UIAudio.PlayOneShot(instance.finishAddingToTotalSFX, 0.5f); } } } private void PlayLineTick() { if (!((Object)(object)_audioSource == (Object)null) && !((Object)(object)_tickClip == (Object)null)) { _audioSource.PlayOneShot(_tickClip, 0.35f); } } private void PlayCeremonyTypeTick() { if (!((Object)(object)_audioSource == (Object)null)) { if ((Object)(object)_ceremonyTickClip == (Object)null) { _ceremonyTickClip = TypewriterAudio.CreateTick(); } _audioSource.PlayOneShot(_ceremonyTickClip, 0.4f); } } private IEnumerator LoadTickAudio() { string[] probed; string path = ResolveTickAudioPath(out probed); if (string.IsNullOrEmpty(path)) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[QuotaUnlockAnnouncement] Tick audio 'digital_text_sequence_terminal_01.wav' not found; the reward ceremony will play without its per-line sting. Probed: " + string.Join(" | ", probed))); } yield break; } UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(new Uri(path).AbsoluteUri, (AudioType)20); try { yield return request.SendWebRequest(); if ((int)request.result != 1) { ManualLogSource log2 = TakeoverBootstrap.Log; if (log2 != null) { log2.LogWarning((object)("[QuotaUnlockAnnouncement] Failed loading tick audio '" + path + "': " + request.error)); } yield break; } _tickClip = DownloadHandlerAudioClip.GetContent(request); if ((Object)(object)_tickClip != (Object)null) { ((Object)_tickClip).name = "Y4NGZ_UnlockAnnouncementTick"; } } finally { ((IDisposable)request)?.Dispose(); } } private static string ResolveTickAudioPath(out string[] probed) { string path = Path.GetDirectoryName(typeof(QuotaUnlockAnnouncement).Assembly.Location) ?? string.Empty; string[] array = (probed = new string[9] { Path.Combine(path, "Audio", "UI", "digital_text_sequence_terminal_01.wav"), Path.Combine(path, "ContractAudio", "UI", "digital_text_sequence_terminal_01.wav"), Path.Combine(Paths.PluginPath, "Y4NGZMonitorTakeover", "Audio", "UI", "digital_text_sequence_terminal_01.wav"), Path.Combine(Paths.PluginPath, "y4ngz-Y4NGZMonitorTakeover", "Audio", "UI", "digital_text_sequence_terminal_01.wav"), Path.Combine(Paths.PluginPath, "Y4NGZCompany", "ContractAudio", "UI", "digital_text_sequence_terminal_01.wav"), Path.Combine(Paths.PluginPath, "y4ngz-Y4NGZCompany", "ContractAudio", "UI", "digital_text_sequence_terminal_01.wav"), Path.Combine(Paths.PluginPath, "y4ngz-Y4NGZUpgrades", "ContractAudio", "UI", "digital_text_sequence_terminal_01.wav"), Path.Combine(Paths.PluginPath, "ContractAudio", "UI", "digital_text_sequence_terminal_01.wav"), Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ContractAudio", "UI", "digital_text_sequence_terminal_01.wav") }); foreach (string text in array) { if (File.Exists(text)) { return text; } } return null; } } internal static class QuotaUnlockAnnouncementPolicy { internal static bool ShouldDispatch(int quota, int lastAnnouncedQuota, bool debugReplay) { if (quota > 0) { if (!debugReplay) { return quota > lastAnnouncedQuota; } return true; } return false; } internal static bool ShouldPersist(bool debugReplay) { return !debugReplay; } internal static bool ShouldSuppressPresentation(int quota, int lastShownQuota, bool debugReplay) { if (!debugReplay) { return quota == lastShownQuota; } return false; } } internal static class ShipSystemsTakeoverBridge { private static bool _takeoverActive; internal static void BeginExternalMonitorTakeover(Texture takeoverTexture) { if (!_takeoverActive) { _takeoverActive = true; ShipSystemsServices.BeginExternalMonitorTakeover((object)takeoverTexture); } } internal static void UpdateExternalMonitorTakeoverTexture(Texture takeoverTexture) { if (_takeoverActive && !((Object)(object)takeoverTexture == (Object)null)) { ShipSystemsServices.UpdateExternalMonitorTakeoverTexture((object)takeoverTexture); } } internal static void EndExternalMonitorTakeover() { if (_takeoverActive) { _takeoverActive = false; ShipSystemsServices.EndExternalMonitorTakeover(); } } } internal static class TakeoverAudioOverrides { private static readonly Dictionary Cache = new Dictionary(StringComparer.Ordinal); private static readonly HashSet Loading = new HashSet(StringComparer.Ordinal); private static readonly HashSet Failed = new HashSet(StringComparer.Ordinal); private static List _mumble = new List(); private static TakeoverAudioOverrideFile _alarm; private static string _signature = string.Empty; internal static void Prepare(MonoBehaviour host, string[] builtInDialogue) { if (!((Object)(object)host == (Object)null)) { QuotaTakeoverPayload currentPayload = QuotaProgressionRegistry.GetCurrentPayload(builtInDialogue); string text = string.Join("|", currentPayload.AlarmAudioFile, currentPayload.AlarmAudioHash, currentPayload.MumbleAudioFiles, currentPayload.MumbleAudioHashes); if (!string.Equals(text, _signature, StringComparison.Ordinal)) { _signature = text; _alarm = QuotaProgressionRegistry.ResolveTakeoverAlarmAudio(currentPayload); _mumble = QuotaProgressionRegistry.ResolveTakeoverMumbleAudio(currentPayload); } Request(host, _alarm); for (int i = 0; i < _mumble.Count; i++) { Request(host, _mumble[i]); } } } internal static AudioClip GetAlarmClip() { return Resolve(_alarm); } internal static AudioClip[] GetMumbleClips() { if (_mumble.Count == 0) { return Array.Empty(); } List list = new List(_mumble.Count); for (int i = 0; i < _mumble.Count; i++) { AudioClip val = Resolve(_mumble[i]); if ((Object)(object)val != (Object)null) { list.Add(val); } } return list.ToArray(); } internal static AudioClip Resolve(TakeoverAudioOverrideFile file) { if (!file.IsValid) { return null; } if (!Cache.TryGetValue(file.CacheKey, out var value)) { return null; } return value; } internal static void Request(MonoBehaviour host, TakeoverAudioOverrideFile file) { if (file.IsValid) { string cacheKey = file.CacheKey; if (!Cache.ContainsKey(cacheKey) && !Failed.Contains(cacheKey) && !Loading.Contains(cacheKey)) { Loading.Add(cacheKey); host.StartCoroutine(LoadClip(file)); } } } private static IEnumerator LoadClip(TakeoverAudioOverrideFile file) { string key = file.CacheKey; string name = Path.GetFileName(file.Path); AudioType val = AudioTypeFor(file.Path); if ((int)val == 0) { Loading.Remove(key); Failed.Add(key); ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[TakeoverAudioOverrides] '" + name + "' has no supported audio format; using the built-in clip.")); } yield break; } UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(new Uri(file.Path).AbsoluteUri, val); try { yield return request.SendWebRequest(); Loading.Remove(key); if ((int)request.result != 1) { Failed.Add(key); ManualLogSource log2 = TakeoverBootstrap.Log; if (log2 != null) { log2.LogWarning((object)("[TakeoverAudioOverrides] Failed loading '" + name + "': " + request.error + "; using the built-in clip.")); } yield break; } AudioClip content = DownloadHandlerAudioClip.GetContent(request); if ((Object)(object)content == (Object)null) { Failed.Add(key); ManualLogSource log3 = TakeoverBootstrap.Log; if (log3 != null) { log3.LogWarning((object)("[TakeoverAudioOverrides] '" + name + "' decoded to no clip; using the built-in clip.")); } yield break; } ((Object)content).name = "Y4NGZ_TakeoverOverride_" + Path.GetFileNameWithoutExtension(file.Path); Cache[key] = content; ManualLogSource log4 = TakeoverBootstrap.Log; if (log4 != null) { log4.LogInfo((object)$"[TakeoverAudioOverrides] Loaded '{name}' ({content.length:F2}s) for the quota takeover."); } } finally { ((IDisposable)request)?.Dispose(); } } private static AudioType AudioTypeFor(string path) { return (AudioType)((Path.GetExtension(path) ?? string.Empty).ToLowerInvariant() switch { ".mp3" => 13, ".wav" => 20, ".ogg" => 14, _ => 0, }); } } internal class TakeoverManager : MonoBehaviour { private sealed class TakeoverExtraMediaPlayer { internal int Index; internal VideoPlayer Player; internal RenderTexture Texture; internal Texture2D ImageTexture; internal string Source; internal Coroutine Watchdog; internal Coroutine MirrorLoop; } private readonly struct TakeoverMediaPaintSite { internal readonly TakeoverMediaSurfaceKey Key; internal readonly Material Material; internal readonly RawImage Overlay; internal TakeoverMediaPaintSite(TakeoverMediaSurfaceKey key, Material material, RawImage overlay) { Key = key; Material = material; Overlay = overlay; } } private Coroutine _takeoverCoroutine; private Coroutine _flashCoroutine; private bool _takeoverActiveState; private string[] _activeDialoguePool; private Action _onTakeoverFinished; private readonly List<(Behaviour graphic, bool wasEnabled)> _disabledMonitorGraphics = new List<(Behaviour, bool)>(); private bool _levelDescWasEnabled; private VideoClip _savedVideoClip; private bool _savedVideoLooping; private bool _savedVideoPlaying; private readonly List<(MeshRenderer renderer, Texture originalTexture)> _overriddenRenderers = new List<(MeshRenderer, Texture)>(); private GeneralImprovementsMonitorLease _generalImprovementsMonitorLease; private Coroutine _generalImprovementsLeaseCoroutine; private static readonly WaitForSecondsRealtime GiLeaseRefresh = new WaitForSecondsRealtime(0.1f); private readonly Dictionary _savedHDRPState = new Dictionary(); private readonly List _topMonitorOverlayImages = new List(); private RawImage _videoReelRawImage; private Texture _savedVideoReelTexture; private readonly List _suppressedHUDGroups = new List(); private readonly List _addedHUDGroups = new List(); private readonly List _hiddenHotbarObjects = new List(); private bool _hudHidden; private RenderTexture _mapScreenRT; private Coroutine _mapBlitCoroutine; private readonly List<(Light light, float origIntensity, Color origColor)> _savedLights = new List<(Light, float, Color)>(); private float _savedSpeakerVolume = 1f; private bool _speakerVolumeSaved; private readonly List<(Behaviour component, bool wasEnabled)> _disabledComponents = new List<(Behaviour, bool)>(); private VideoPlayer _videoPlayer; private RenderTexture _renderTexture; private bool _usingConfiguredVideoSource; private bool _looseDefaultVideoInUse; private bool _mediaLengthPending; private double _mediaLengthSeconds; private Coroutine _videoFallbackCoroutine; private readonly List _extraMediaPlayers = new List(); private readonly List _mediaPaintSites = new List(); private TakeoverMediaPlan _mediaPlan; private const int GiSurfaceTextureSlots = 32; private AudioSource _mumbleSource; private AudioSource _alarmSource; private AudioSource _droneSource; private GameObject _dialogueCanvasGO; private Texture2D _customImageTexture; private bool _useQuotaProgressionPayload; private TakeoverSoundtrackPlan _soundtrackPlan; private AudioSource _soundtrackSource; private VideoPlayer _soundtrackPlayer; private RenderTexture _soundtrackDummyRT; private Coroutine _soundtrackCoroutine; private static readonly string[] DialoguePool = new string[21] { "First satisfactory output from this crew.\nClean execution. I had the over on you\nfinishing this one alive, paid out nicely.\nReinvesting immediately. I want you to know\nI don't celebrate. Celebration implies surprise.\nI'm not surprised. I'm confirmed.", "Consistent output. I respect the margins\non this run. Reminds me of when I wrote\nTech Talk — calculated, efficient, no wasted\nbars. Every word in that track had a function.\nEvery syllable was load-bearing. That's what\nI need from you. Load-bearing performance.\nNo filler. No ad-libs. Just execution.", "You're demonstrating real value. I moved\nsome numbers around on the back end,\nnothing you need to worry about. Just keep\nperforming. I restructured some of the internal\nmetrics to better reflect what I'm seeing out\nthere. Again, not your concern. Your concern\nis scrap. My concern is everything else.", "I was in New York closing something.\nCan't say what. Point is, I had action on\nfour different outcomes this week and every\nsingle one hit. You were one of them.\nCongratulations. I sat in a steakhouse in\nMurray Hill afterward and ate alone. Not\nbecause I had to. Because the booth across\nfrom me was occupied by a version of me\nwho didn't need to eat. We didn't speak.", "I want you to understand something.\nJuice WRLD recorded hundreds of songs that\nnever came out. Hundreds. That's not waste.\nThat's pipeline. You are pipeline. Keep moving.\nPeople called him prolific. I call him\noperationally sound. He understood throughput.\nMost artists don't. Most crews don't either.", "I've been running the numbers on crew\nsurvivability and I'll be honest, you're\noutperforming the model. I don't like when\nthings outperform the model. It means the\nmodel is wrong. I'll adjust. I've already\nstarted recalibrating. By tomorrow morning\nthe model will expect exactly what you\ndelivered today. You'll have to do better.\nThat's not a threat. That's forecasting.", "Wrote Bruises about a very specific feeling.\nNot pain exactly. More like the moment you\nrealize pain is just overhead. You account\nfor it. You build it into the forecast.\nQuota's done. People thought that song was\nabout a relationship. It was about depreciation.\nPhysical, emotional, structural. Everything\nbruises. The question is whether you write\nit off or capitalize it.", "I placed a parlay — crew survival, scrap\ntotal, and whether anyone would cry on the\nship. Two out of three hit. I won't say which\none missed. The book I use doesn't ask\nquestions. I like that about them. They\nunderstand that a bet is just a belief with\ncollateral. I believe in very specific things.\nYou should find that comforting.", "Took a red-eye to New York. Sat in a booth\nat a restaurant in Midtown for four hours.\nDidn't order anything. Waitress asked if I was\nokay. I said 'Yeah what.' She didn't ask again.\nGood trip overall. Flew back the same night.\nDidn't sleep. Wrote half a verse on the plane\nabout lunar rotation cycles. Scrapped it.\nToo honest.", "I'm going to be transparent with you\nbecause I think you've earned it. Magic Trik\nisn't about magic. It's about making people\nlook where you want them to look. That's all\nmanagement is. Misdirection with benefits.\nI learned that in New York. I learned it again\non Titan. Some lessons you have to learn in\ntwo places before they stick.", "I have a spreadsheet. You're on it.\nEveryone's on it. The columns are labeled\nbut I won't share what the labels mean.\nYour column is trending upward. Be grateful\nfor that. I update it manually. Not because\nI have to. The automation works fine. I just\nlike the feeling of typing the numbers myself.\nIt keeps me connected to the data. The data\nkeeps me connected to you.", "Juice never made it this far. Different\ncontext. But I think about it. I think about\nwhat it means to keep going past the point\nwhere someone else stopped. Not better.\nJust still here. Still filing. Still submitting.\nThere's a version of Rockstar Status I never\nreleased where the second verse is just me\nreading quarterly projections over an 808.\nIt goes harder than you'd think.", "I shorted three crews this cycle. Not yours.\nYours I'm long on. That's not loyalty.\nThat's positioning. Rockstar Status was about\npositioning. Nobody understood that. They\nheard the hook and thought it was about fame.\nIt was about leverage. Every bar in that song\nis a hedge against irrelevance. I don't write\nmusic. I write instruments. Financial ones.", "I stood in the ship hallway for forty-five\nminutes this morning. Not thinking. Not\nwaiting. Just auditing. Internally. Everything\nchecks out. Everything always checks out.\nThat's what concerns me. When the numbers\nare too clean it means something isn't being\nmeasured. I've started measuring things that\ndon't have units yet. I'll name the units later.\nOne of them might be named after you.", "Someone asked me what Y4NGZ stands for.\nI told them it doesn't stand for anything.\nThey said everything has to mean something.\nI fired them. Not from a job. Just in general.\nFrom everything. They still exist but in a\nreduced capacity. I think about that interaction\nwhen I'm trying to fall asleep. Not because\nit bothers me. Because it's the only thing\nthat's quiet enough.", "The line on the next quota is steep but\nI've seen your work and frankly the house\nis wrong. The house is always wrong.\nI am the house. I am wrong. This is fine.\nI built the odds myself. Used a model I wrote\non a napkin in JFK. The napkin is framed in\nmy studio now. It's the most important thing\nI've ever written and it's just numbers.", "New York again. Times Square. I looked up\nat one of those billboards and for a second\nit said something meant only for me. Then it\nwent back to normal. I placed a bet on whether\nit would happen again. It did. I'm up on the\nyear. I'm up on the lifetime. The problem is\nI don't know what I'm winning. The money goes\nsomewhere. I go somewhere. We never end up\nin the same place.", "I don't have anything to say right now.\nBut I'm choosing to be here. That's worse\nand I need you to understand that. Silence\nfrom me isn't empty. It's full. It's a room\nwith no furniture that still feels crowded.\nThat's what the next album sounds like.\nYou'll never hear it.", "The next song is called Margin Call.\nIt's three minutes of silence over an 808.\nThe silence is the point. The 808 is for the\nshareholders. I played it for someone once\nand they left the room. Not because it was\nbad. Because they understood it. Understanding\nis the most violent thing you can do to\na person.", "I ran the numbers on your life expectancy.\nBet the under. Nothing personal. Everything\npersonal. Quota's done. I have a policy of\nnever apologizing for a position I've taken.\nThe position is the apology. The odds are the\nexplanation. If you survive long enough I'll\nclose the bet at a loss and I will feel nothing\nabout it. That's not cold. That's solvent.", "Every moon is New York if you think about\nit long enough. I have thought about it long\nenough. The same grid. The same strangers.\nThe same feeling that you're being watched\nby something that doesn't care about you\nspecifically but cares about what you represent\nstatistically. I wrote a hook about it.\nIt goes hard. You'll never hear it." }; private const float MinAlarmLength = 1f; private const float VideoPrimeGraceSeconds = 1f; private const float ConfiguredVideoFallbackSeconds = 4f; private const float MediaLengthPrepareGraceSeconds = 30f; private const float MediaLengthMaxSeconds = 1800f; private const float SignalLostAfterSeconds = 6f; private const float PrepareRetrySeconds = 2f; private Coroutine _signalLostCoroutine; private Texture2D[] _signalLostFrames; internal static TakeoverManager Instance { get; private set; } private bool _takeoverActive { get { return _takeoverActiveState; } set { if (_takeoverActiveState != value) { _takeoverActiveState = value; if (value) { TakeoverMonitorOwnership.ClaimWall("monitor takeover"); } else { TakeoverMonitorOwnership.ReleaseWall(); } } } } private int ActiveMediaPlayerCount => _extraMediaPlayers.Count + 1; private bool SoundtrackSilencesBed => _soundtrackPlan.Silences; internal static bool IsActive { get { if ((Object)(object)Instance != (Object)null) { return Instance._takeoverActive; } return false; } } public static event Action OnTakeoverEnded; private void Awake() { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { ForceRestore("OnDestroy"); TakeoverMonitorOwnership.ReleaseWall(); Instance = null; } } internal static void EnsureInstance() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Instance != (Object)null)) { new GameObject("Y4NGZ_TakeoverManager").AddComponent(); } } internal void BeginTakeover(bool skipOrbitDelay = false) { if (!_takeoverActive) { _useQuotaProgressionPayload = true; QuotaProgressionRegistry.PrepareHostTakeoverPayload(DialoguePool); TakeoverAudioOverrides.Prepare((MonoBehaviour)(object)this, DialoguePool); TakeoverSoundtrack.Prepare((MonoBehaviour)(object)this); _activeDialoguePool = DialoguePool; _onTakeoverFinished = null; if (_takeoverCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_takeoverCoroutine); } _takeoverCoroutine = ((MonoBehaviour)this).StartCoroutine(TakeoverSequence(skipOrbitDelay ? 0f : TakeoverBootstrap.CfgOrbitDelay.Value)); } } internal void BeginMaskManTakeover(bool skipOrbitDelay = false, bool persistCompletion = true) { if (_takeoverActive) { return; } _useQuotaProgressionPayload = false; _activeDialoguePool = DialoguePool; _onTakeoverFinished = null; if ((Object)(object)TakeoverBootstrap.MaskManVideoClip == (Object)null || (Object)(object)TakeoverBootstrap.MaskManAudioClip == (Object)null) { TakeoverBootstrap.Log.LogError((object)("[TakeoverManager] BeginMaskManTakeover: missing assets " + $"(video={(Object)(object)TakeoverBootstrap.MaskManVideoClip != (Object)null}, audio={(Object)(object)TakeoverBootstrap.MaskManAudioClip != (Object)null}). Aborting.")); return; } if (_takeoverCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_takeoverCoroutine); } _takeoverCoroutine = ((MonoBehaviour)this).StartCoroutine(MaskManSequence(skipOrbitDelay ? 0f : TakeoverBootstrap.CfgOrbitDelay.Value, persistCompletion)); } internal void ForceRestore(string reason) { if (_takeoverActive) { QuotaUnlockAnnouncement.ClearDebugReplay(); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] ForceRestore: " + reason)); if (_takeoverCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_takeoverCoroutine); _takeoverCoroutine = null; } if (_flashCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_flashCoroutine); _flashCoroutine = null; } _onTakeoverFinished = null; _activeDialoguePool = DialoguePool; RestoreAll(immediate: true); _takeoverActive = false; } } private IEnumerator TakeoverSequence(float orbitDelay) { _takeoverActive = true; _mediaLengthPending = false; _mediaLengthSeconds = 0.0; _soundtrackPlan = default(TakeoverSoundtrackPlan); if (orbitDelay > 0f) { yield return (object)new WaitForSeconds(orbitDelay); } if ((Object)(object)StartOfRound.Instance == (Object)null || !StartOfRound.Instance.inShipPhase) { _takeoverActive = false; yield break; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance.speakerAudioSource != (Object)null) { _savedSpeakerVolume = instance.speakerAudioSource.volume; instance.speakerAudioSource.volume = 0f; _speakerVolumeSaved = true; } float duration = TakeoverBootstrap.CfgTakeoverDuration.Value; float elapsed = 0f; if (_useQuotaProgressionPayload) { TakeoverAudioOverrides.Prepare((MonoBehaviour)(object)this, DialoguePool); TakeoverSoundtrack.Prepare((MonoBehaviour)(object)this); } _soundtrackPlan = (_useQuotaProgressionPayload ? TakeoverSoundtrack.ResolvePlan(QuotaProgressionRegistry.GetCurrentPayload(DialoguePool).MediaFile) : default(TakeoverSoundtrackPlan)); OpenBodyCamsCompat.Suppress(); SetupVideo(); StartSoundtrack(); _mediaPlan = (_useQuotaProgressionPayload ? TakeoverMediaPlan.Resolve() : default(TakeoverMediaPlan)); if (_mediaPlan.Active) { SetupAdditionalMediaPlayers(_mediaPlan, QuotaProgressionRegistry.LoopMedia); } TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] Seq: calling PlayPowerDownWhine."); PlayPowerDownWhine(); if (TakeoverBootstrap.CfgDimLights.Value) { yield return ((MonoBehaviour)this).StartCoroutine(DimLights()); _flashCoroutine = ((MonoBehaviour)this).StartCoroutine(FlashLightsLoop()); } else { PlayAlarm(); } TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] Seq: calling StartDrone."); StartDrone(); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] Seq: StartDrone returned. _droneSource=" + (((Object)(object)_droneSource != (Object)null) ? "OK" : "NULL") + " clip=" + (((Object)(object)_droneSource != (Object)null && (Object)(object)_droneSource.clip != (Object)null) ? ((Object)_droneSource.clip).name : "") + " " + $"isPlaying={(Object)(object)_droneSource != (Object)null && _droneSource.isPlaying}")); if (TakeoverBootstrap.CfgHideHUD.Value) { HideHUD(); } yield return ((MonoBehaviour)this).StartCoroutine(PrimeVideoForDisplay()); if (_useQuotaProgressionPayload && QuotaProgressionRegistry.UseMediaLengthAsDuration && _usingConfiguredVideoSource) { ((MonoBehaviour)this).StartCoroutine(AdoptConfiguredMediaLength()); } ManualLogSource log = TakeoverBootstrap.Log; string[] obj = new string[6] { "[Diag/RT] at OverrideMonitors: _renderTexture id=", ((Object)(object)_renderTexture != (Object)null) ? ((Object)_renderTexture).GetInstanceID().ToString() : "NULL", " VP.targetTexture id=", null, null, null }; VideoPlayer videoPlayer = _videoPlayer; obj[3] = (((Object)(object)((videoPlayer != null) ? videoPlayer.targetTexture : null) != (Object)null) ? ((Object)_videoPlayer.targetTexture).GetInstanceID().ToString() : "NULL"); obj[4] = " "; VideoPlayer videoPlayer2 = _videoPlayer; obj[5] = $"match={(Object)(object)((videoPlayer2 != null) ? videoPlayer2.targetTexture : null) == (Object)(object)_renderTexture}"; log.LogInfo((object)string.Concat(obj)); ShipSystemsTakeoverBridge.BeginExternalMonitorTakeover((Texture)(object)_renderTexture); OverrideMonitors(); ((MonoBehaviour)this).StartCoroutine(DiagVideoStateLoop()); ((MonoBehaviour)this).StartCoroutine(DiagRTReadbackAfterDelay(3f)); TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] Seq: starting PlayMumbleSequence coroutine."); if (!SoundtrackSilencesBed) { ((MonoBehaviour)this).StartCoroutine(PlayMumbleSequence()); } string[] array = _activeDialoguePool ?? DialoguePool; string passage = ((!_useQuotaProgressionPayload) ? array[Random.Range(0, array.Length)] : QuotaProgressionRegistry.GetCurrentPayload(DialoguePool).Dialogue); ((MonoBehaviour)this).StartCoroutine(ShowDialogue(passage)); while (elapsed < duration || _mediaLengthPending) { if (_mediaLengthSeconds > 0.0) { duration = (float)_mediaLengthSeconds; } elapsed += Time.deltaTime; yield return null; } if (_flashCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_flashCoroutine); _flashCoroutine = null; } yield return ((MonoBehaviour)this).StartCoroutine(CRTTransition()); yield return ((MonoBehaviour)this).StartCoroutine(RestoreCoroutine()); _takeoverActive = false; _takeoverCoroutine = null; Action onTakeoverFinished = _onTakeoverFinished; _onTakeoverFinished = null; _activeDialoguePool = DialoguePool; try { onTakeoverFinished?.Invoke(); } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] onFinished callback failed: " + ex.Message)); } RaiseTakeoverEnded(); } private static void RaiseTakeoverEnded() { try { TakeoverManager.OnTakeoverEnded?.Invoke(); } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] OnTakeoverEnded handler failed: " + ex.Message)); } } private IEnumerator MaskManSequence(float orbitDelay, bool persistCompletion) { _takeoverActive = true; if (orbitDelay > 0f) { yield return (object)new WaitForSeconds(orbitDelay); } if ((Object)(object)StartOfRound.Instance == (Object)null || !StartOfRound.Instance.inShipPhase) { _takeoverActive = false; _takeoverCoroutine = null; yield break; } if ((Object)(object)TakeoverBootstrap.MaskManVideoClip == (Object)null || (Object)(object)TakeoverBootstrap.MaskManAudioClip == (Object)null) { TakeoverBootstrap.Log.LogError((object)"[TakeoverManager] MaskManSequence: assets null at runtime — aborting."); RestoreAll(immediate: true); _takeoverActive = false; _takeoverCoroutine = null; yield break; } StartOfRound sor = StartOfRound.Instance; if ((Object)(object)sor.speakerAudioSource != (Object)null) { _savedSpeakerVolume = sor.speakerAudioSource.volume; sor.speakerAudioSource.volume = 0f; _speakerVolumeSaved = true; } float duration = TakeoverBootstrap.MaskManAudioClip.length + TakeoverBootstrap.CfgMaskManTailBuffer.Value; float elapsed = 0f; OpenBodyCamsCompat.Suppress(); SetupVideoFor(TakeoverBootstrap.MaskManVideoClip, looping: true); TakeoverBootstrap.Log.LogInfo((object)($"[TakeoverManager] MaskMan: duration={duration:F2}s " + $"(audio={TakeoverBootstrap.MaskManAudioClip.length:F2}s + buffer={TakeoverBootstrap.CfgMaskManTailBuffer.Value:F2}s).")); PlayPowerDownWhine(); if (TakeoverBootstrap.CfgDimLights.Value) { yield return ((MonoBehaviour)this).StartCoroutine(DimLights()); _flashCoroutine = ((MonoBehaviour)this).StartCoroutine(FlashLightsLoop()); } else { PlayAlarm(); } StartDrone(); if (TakeoverBootstrap.CfgHideHUD.Value) { HideHUD(); } yield return ((MonoBehaviour)this).StartCoroutine(PrimeVideoForDisplay()); ShipSystemsTakeoverBridge.BeginExternalMonitorTakeover((Texture)(object)_renderTexture); OverrideMonitors(); GameObject val = new GameObject("MaskMan_Audio"); Object.DontDestroyOnLoad((Object)(object)val); _mumbleSource = val.AddComponent(); if ((Object)(object)sor?.speakerAudioSource != (Object)null) { val.transform.position = ((Component)sor.speakerAudioSource).transform.position; } else if ((Object)(object)sor != (Object)null) { val.transform.position = ((Component)sor).transform.position; } _mumbleSource.spatialBlend = 0f; _mumbleSource.volume = 1f; _mumbleSource.loop = false; _mumbleSource.playOnAwake = false; _mumbleSource.PlayOneShot(TakeoverBootstrap.MaskManAudioClip); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] MaskMan: PlayOneShot '" + ((Object)TakeoverBootstrap.MaskManAudioClip).name + "' " + $"({TakeoverBootstrap.MaskManAudioClip.length:F2}s) isPlaying={_mumbleSource.isPlaying}.")); if (persistCompletion) { try { string text = GameNetworkManager.Instance?.currentSaveFileName; if (!string.IsNullOrEmpty(text)) { ES3.Save("Y4NGZ_MaskManFired", true, text); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] Persisted Y4NGZ_MaskManFired=true to save '" + text + "'.")); } else { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] Could not persist Y4NGZ_MaskManFired — currentSaveFileName is null."); } } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Failed to persist Y4NGZ_MaskManFired: " + ex.Message)); } } while (elapsed < duration) { elapsed += Time.deltaTime; yield return null; } if (_flashCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_flashCoroutine); _flashCoroutine = null; } yield return ((MonoBehaviour)this).StartCoroutine(CRTTransition()); yield return ((MonoBehaviour)this).StartCoroutine(RestoreCoroutine()); _takeoverActive = false; _takeoverCoroutine = null; RaiseTakeoverEnded(); } private IEnumerator DimLights() { _savedLights.Clear(); GameObject val = GameObject.Find("Environment/HangarShip") ?? GameObject.Find("HangarShip"); Light[] array = (((Object)(object)val != (Object)null) ? val.GetComponentsInChildren(true) : Object.FindObjectsOfType()); foreach (Light val2 in array) { if (!((Object)(object)val2 == (Object)null) && ((Component)val2).gameObject.activeInHierarchy) { _savedLights.Add((val2, val2.intensity, val2.color)); } } PlayAlarm(); float dimDuration = 1.5f; float elapsed = 0f; float target = TakeoverBootstrap.CfgLightDimIntensity.Value; while (elapsed < dimDuration) { elapsed += Time.deltaTime; float num = Mathf.SmoothStep(0f, 1f, elapsed / dimDuration); foreach (var (val3, num2, val4) in _savedLights) { if (!((Object)(object)val3 == (Object)null)) { val3.intensity = Mathf.Lerp(num2, num2 * target, num); val3.color = Color.Lerp(val4, new Color(Mathf.Min(val4.r + 0.15f, 1f), val4.g * 0.7f, val4.b * 0.7f, val4.a), num); } } yield return null; } } private IEnumerator FlashLightsLoop() { float halfPeriod = 1f / 6f; bool bright = false; while (_takeoverActive) { bright = !bright; foreach (var (val, num, val2) in _savedLights) { if (!((Object)(object)val == (Object)null)) { if (bright) { val.intensity = num * 0.6f; val.color = new Color(0.1f, 1f, 0.1f, val2.a); } else { val.intensity = num * 0.03f; val.color = new Color(0f, 0.4f, 0f, val2.a); } } } yield return (object)new WaitForSeconds(halfPeriod); } } private void PlayAlarm() { //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Expected O, but got Unknown //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) if (SoundtrackSilencesBed) { return; } AudioClip val = null; string text = null; if (_useQuotaProgressionPayload) { val = TakeoverAudioOverrides.GetAlarmClip(); if ((Object)(object)val != (Object)null) { text = "config:" + ((Object)val).name; TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] PlayAlarm: using configured clip '{((Object)val).name}' ({val.length:F2}s)."); } } if ((Object)(object)val == (Object)null && (Object)(object)TakeoverBootstrap.AlarmClip != (Object)null) { val = TakeoverBootstrap.AlarmClip; text = "bundle:Y4NGZ_Klaxon"; TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] PlayAlarm: using bundle clip '{((Object)val).name}' ({val.length:F2}s)."); } else if ((Object)(object)val == (Object)null) { AudioClip val2 = FindGameAlarmClip(); if ((Object)(object)val2 != (Object)null && val2.length < 1f) { TakeoverBootstrap.Log.LogWarning((object)$"[TakeoverManager] PlayAlarm: rejecting scene clip '{((Object)val2).name}' ({val2.length:F2}s) — shorter than 1s threshold."); val2 = null; } if ((Object)(object)val2 != (Object)null) { val = val2; text = "scene:" + ((Object)val2).name; TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] PlayAlarm: using scene clip '{((Object)val2).name}' ({val2.length:F2}s)."); } else { val = CreateKlaxonClip(); text = "procedural"; TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] PlayAlarm: falling back to procedural klaxon."); } } if ((Object)(object)val == (Object)null) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] PlayAlarm: alarmClip null after fallback — aborting."); return; } TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] Alarm source: " + ((Object)val).name + " (from " + text + ")")); StartOfRound instance = StartOfRound.Instance; Vector3 position = (((Object)(object)instance?.speakerAudioSource != (Object)null) ? ((Component)instance.speakerAudioSource).transform.position : (((Object)(object)instance != (Object)null) ? ((Component)instance).transform.position : Vector3.zero)); GameObject val3 = new GameObject("Y4NGZ_Alarm"); Object.DontDestroyOnLoad((Object)(object)val3); _alarmSource = val3.AddComponent(); ((Component)_alarmSource).transform.position = position; _alarmSource.spatialBlend = 0f; _alarmSource.volume = 0.9f; _alarmSource.clip = val; _alarmSource.loop = true; _alarmSource.Play(); } private static AudioClip FindGameAlarmClip() { TakeoverBootstrap.Log.LogDebug((object)"[TakeoverManager] FindGameAlarmClip: beginning scan."); ConfigEntry cfgVerboseLogging = TakeoverBootstrap.CfgVerboseLogging; if (cfgVerboseLogging != null && cfgVerboseLogging.Value) { try { AudioSource[] array = Resources.FindObjectsOfTypeAll(); TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] scene AudioSources found: {array.Length}"); int num = 0; AudioSource[] array2 = array; foreach (AudioSource val in array2) { if (!((Object)(object)val == (Object)null)) { string text = (((Object)(object)val.clip != (Object)null) ? ((Object)val.clip).name : ""); float num2 = (((Object)(object)val.clip != (Object)null) ? val.clip.length : 0f); string text2 = (((Object)(object)((Component)val).gameObject != (Object)null) ? ((Object)((Component)val).gameObject).name : ""); TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] AudioSource on '{text2}' clip='{text}' len={num2:F2}s spatial={val.spatialBlend:F2}"); if (++num > 200) { TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] (truncated AudioSource list at 200)"); break; } } } } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] AudioSource dump failed: " + ex.Message)); } } AudioClip[] array3 = Resources.FindObjectsOfTypeAll(); TakeoverBootstrap.Log.LogDebug((object)$"[TakeoverManager] loaded AudioClips found: {array3.Length}"); string[] array4 = new string[9] { "ShipAlarmHornConstant", "0DaysLeftAlert", "DeadlineAlarm", "FireAlarm", "ShipAlarm", "RedAlarmSFX", "ShipAlarmSFX", "EjectAlarm", "ShipAlarmCord" }; AudioClip[] array5; foreach (string text3 in array4) { array5 = array3; foreach (AudioClip val2 in array5) { if (!((Object)(object)val2 == (Object)null) && ((Object)val2).name.Equals(text3, StringComparison.OrdinalIgnoreCase)) { if (!(val2.length < 1f)) { TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] exact MATCH '{((Object)val2).name}' ({val2.length:F2}s) via keyword '{text3}'."); return val2; } TakeoverBootstrap.Log.LogDebug((object)$"[TakeoverManager] exact '{((Object)val2).name}' matched but rejected ({val2.length:F2}s < {1f:F2}s)."); } } } TakeoverBootstrap.Log.LogDebug((object)"[TakeoverManager] no exact match — falling to keyword search."); AudioClip val3 = null; string arg = null; array5 = array3; foreach (AudioClip val4 in array5) { if ((Object)(object)val4 == (Object)null || val4.length < 1f) { continue; } string text4 = ((Object)val4).name.ToLower(); if (text4.Contains("chatter") || text4.Contains("foot") || text4.Contains("spot") || text4.Contains("siren") || text4.Contains("tentacle") || text4.Contains("insanity") || text4.Contains("bomb") || text4.Contains("lrad") || text4.Contains("extension") || text4.Contains("controller")) { continue; } string text5 = null; if (text4.Contains("alarm")) { text5 = "alarm"; } else if (text4.Contains("klaxon")) { text5 = "klaxon"; } else if (text4.Contains("eject")) { text5 = "eject"; } else if (text4.Contains("alert")) { text5 = "alert"; } else if (text4.Contains("emergency")) { text5 = "emergency"; } if (text5 != null) { TakeoverBootstrap.Log.LogDebug((object)$"[TakeoverManager] keyword candidate '{((Object)val4).name}' ({val4.length:F2}s) matched='{text5}'."); if ((Object)(object)val3 == (Object)null || val4.length > val3.length) { val3 = val4; arg = text5; } } } if ((Object)(object)val3 != (Object)null) { TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] keyword WINNER '{((Object)val3).name}' ({val3.length:F2}s) via '{arg}'."); return val3; } TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] FindGameAlarmClip: no suitable clip — caller will fall back."); return null; } private static AudioClip CreateKlaxonClip() { int num = 44100; float num2 = 0.5f; int num3 = (int)((float)num * num2); int num4 = 5; int num5 = num3 * num4; float[] array = new float[num5]; float num6 = 0f; float num7 = 0f; for (int i = 0; i < num5; i++) { float num8 = (float)(i % num3) / (float)num3; float num9 = ((num8 < 0.5f) ? Mathf.Lerp(400f, 800f, num8 * 2f) : Mathf.Lerp(800f, 400f, (num8 - 0.5f) * 2f)); num6 += num9 / (float)num; num7 += num9 * 2f / (float)num; float num10 = ((Mathf.Sin(num6 * 2f * MathF.PI) > 0f) ? 1f : (-1f)); float num11 = Mathf.Sin(num7 * 2f * MathF.PI); float num12 = num10 * 0.6f + num11 * 0.25f; float num13 = (((float)i < (float)num * 0.01f) ? ((float)i / ((float)num * 0.01f)) : 1f); array[i] = num12 * num13 * 0.8f; } AudioClip obj = AudioClip.Create("Y4NGZ_Klaxon", num5, 1, num, false); obj.SetData(array, 0); return obj; } private void PlayPowerDownWhine() { //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected O, but got Unknown if (!SoundtrackSilencesBed) { int num = 44100; float num2 = 1.4f; int num3 = (int)((float)num * num2); float[] array = new float[num3]; float num4 = 0f; float num5 = 0f; for (int i = 0; i < num3; i++) { float num6 = (float)i / (float)num3; float num7 = Random.value * 2f - 1f; num4 += 0.057f * (num7 - num4); float num8 = num4 * 1.8f; num5 += 45f / (float)num; float num9 = Mathf.Exp((0f - num6) * 6f); float num10 = Mathf.Sin(num5 * 2f * MathF.PI) * num9 * 0.6f; float num11 = Mathf.Clamp01(1f - num6 * num6); array[i] = (num8 * 0.4f + num10) * num11 * 0.45f; } AudioClip val = AudioClip.Create("Y4NGZ_PowerDown", num3, 1, num, false); val.SetData(array, 0); GameObject val2 = new GameObject("Y4NGZ_PowerDown"); AudioSource obj = val2.AddComponent(); obj.spatialBlend = 0f; obj.volume = 0.55f; obj.PlayOneShot(val); Object.Destroy((Object)val2, num2 + 0.5f); } } private void StartDrone() { //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Expected O, but got Unknown if (SoundtrackSilencesBed) { return; } int num = 44100; float num2 = TakeoverBootstrap.CfgTakeoverDuration.Value + 4f; int num3 = (int)((float)num * num2); float[] array = new float[num3]; float num4 = 0f; float num5 = 0f; float num6 = 0f; for (int i = 0; i < num3; i++) { float num7 = (float)i / (float)num3; num4 += 55f / (float)num; num5 += 82.5f / (float)num; float num8 = Mathf.Sin(num4 * 2f * MathF.PI) * 0.5f + Mathf.Sin(num5 * 2f * MathF.PI) * 0.3f; float num9 = Random.value * 2f - 1f; num6 += 0.0712f * (num9 - num6); float num10 = 0.35f + 0.25f * Mathf.Sin(num7 * num2 * 2f * MathF.PI * 0.4f); float num11 = num6 * num10; float num12 = num8 + num11; float num13 = 0.7f + 0.3f * Mathf.Sin(num7 * num2 * 2f * MathF.PI * 1.5f); float num14 = 1f; if ((float)i < (float)num * 0.5f) { num14 = (float)i / ((float)num * 0.5f); } if (i > num3 - (int)((float)num * 1.5f)) { num14 = (float)(num3 - i) / ((float)num * 1.5f); } array[i] = num12 * num13 * num14 * 0.5f; } AudioClip val = AudioClip.Create("Y4NGZ_Drone", num3, 1, num, false); val.SetData(array, 0); GameObject val2 = new GameObject("Y4NGZ_Drone"); Object.DontDestroyOnLoad((Object)(object)val2); _droneSource = val2.AddComponent(); if ((Object)(object)_droneSource == (Object)null) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] StartDrone: AddComponent returned null."); return; } _droneSource.spatialBlend = 0f; _droneSource.volume = 0.7f; _droneSource.clip = val; _droneSource.loop = false; if ((Object)(object)_droneSource.clip == (Object)null) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] StartDrone: clip not assigned to source."); } _droneSource.Play(); TakeoverBootstrap.Log.LogInfo((object)($"[TakeoverManager] StartDrone: playing '{((Object)val).name}' len={val.length:F2}s " + $"spatial={_droneSource.spatialBlend:F2} vol={_droneSource.volume:F2} isPlaying={_droneSource.isPlaying}")); } private void StartSoundtrack() { //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Expected O, but got Unknown //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Expected O, but got Unknown //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Expected O, but got Unknown switch (_soundtrackPlan.Kind) { case TakeoverSoundtrackKind.None: break; case TakeoverSoundtrackKind.LocalClip: { AudioClip clip = TakeoverSoundtrack.GetClip(); if ((Object)(object)clip == (Object)null) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] StartSoundtrack: the resolved soundtrack clip disappeared; using the built-in audio."); _soundtrackPlan = default(TakeoverSoundtrackPlan); break; } AudioSource val2 = EnsureSoundtrackSource(); if ((Object)(object)val2 == (Object)null) { _soundtrackPlan = default(TakeoverSoundtrackPlan); break; } val2.clip = clip; val2.loop = true; val2.Play(); TakeoverBootstrap.Log.LogInfo((object)($"[TakeoverManager] StartSoundtrack: playing '{((Object)clip).name}' ({clip.length:F2}s) " + $"mode={_soundtrackPlan.Mode} vol={_soundtrackPlan.Volume:F2}.")); break; } case TakeoverSoundtrackKind.ReuseVideoTrack: if (!_usingConfiguredVideoSource) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] StartSoundtrack: the shared soundtrack/media video is not playing, so there is no track to reuse; using the built-in audio."); _soundtrackPlan = default(TakeoverSoundtrackPlan); } else { TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] StartSoundtrack: reusing the takeover video's own audio track " + $"(mode={_soundtrackPlan.Mode} vol={_soundtrackPlan.Volume:F2}).")); } break; case TakeoverSoundtrackKind.YoutubeCached: { AudioSource val = EnsureSoundtrackSource(); if ((Object)(object)val == (Object)null) { _soundtrackPlan = default(TakeoverSoundtrackPlan); break; } try { _soundtrackDummyRT = new RenderTexture(4, 4, 0, (RenderTextureFormat)0); _soundtrackDummyRT.Create(); _soundtrackPlayer = ((Component)this).gameObject.AddComponent(); _soundtrackPlayer.playOnAwake = false; _soundtrackPlayer.source = (VideoSource)1; _soundtrackPlayer.url = new Uri(_soundtrackPlan.ResolvedPath).AbsoluteUri; _soundtrackPlayer.renderMode = (VideoRenderMode)2; _soundtrackPlayer.targetTexture = _soundtrackDummyRT; _soundtrackPlayer.isLooping = true; _soundtrackPlayer.skipOnDrop = true; _soundtrackPlayer.waitForFirstFrame = false; _soundtrackPlayer.audioOutputMode = (VideoAudioOutputMode)1; _soundtrackPlayer.controlledAudioTrackCount = 1; _soundtrackPlayer.SetTargetAudioSource((ushort)0, val); _soundtrackPlayer.errorReceived -= new ErrorEventHandler(OnSoundtrackErrorReceived); _soundtrackPlayer.errorReceived += new ErrorEventHandler(OnSoundtrackErrorReceived); _soundtrackPlayer.Prepare(); _soundtrackPlayer.Play(); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] StartSoundtrack: driving the cached soundtrack video's audio track " + $"(mode={_soundtrackPlan.Mode} vol={_soundtrackPlan.Volume:F2}).")); break; } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] StartSoundtrack: the hidden soundtrack player failed to start (" + ex.GetType().Name + "); using the built-in audio.")); _soundtrackPlan = default(TakeoverSoundtrackPlan); break; } } } } private IEnumerator SoundtrackLoopWatch() { while (_takeoverActive && (Object)(object)_soundtrackSource != (Object)null) { if (_soundtrackPlan.Kind == TakeoverSoundtrackKind.LocalClip && (Object)(object)_soundtrackSource.clip != (Object)null && !_soundtrackSource.isPlaying) { _soundtrackSource.Play(); } yield return (object)new WaitForSeconds(0.5f); } } private void StopSoundtrack(string reason) { if (_soundtrackCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_soundtrackCoroutine); _soundtrackCoroutine = null; } if ((Object)(object)_soundtrackPlayer != (Object)null) { try { _soundtrackPlayer.Stop(); } catch { } } if ((Object)(object)_soundtrackSource != (Object)null) { try { _soundtrackSource.Stop(); } catch { } } if (!string.IsNullOrEmpty(reason) && _soundtrackPlan.Active) { TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] StopSoundtrack: " + reason + ".")); } } private AudioSource EnsureSoundtrackSource() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown if ((Object)(object)_soundtrackSource != (Object)null) { return _soundtrackSource; } GameObject val = new GameObject("Y4NGZ_Soundtrack"); Object.DontDestroyOnLoad((Object)(object)val); _soundtrackSource = val.AddComponent(); if ((Object)(object)_soundtrackSource == (Object)null) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] EnsureSoundtrackSource: AddComponent returned null."); Object.Destroy((Object)(object)val); return null; } _soundtrackSource.spatialBlend = 0f; _soundtrackSource.volume = _soundtrackPlan.Volume; _soundtrackSource.loop = true; _soundtrackSource.playOnAwake = false; _soundtrackCoroutine = ((MonoBehaviour)this).StartCoroutine(SoundtrackLoopWatch()); return _soundtrackSource; } private void OnSoundtrackErrorReceived(VideoPlayer source, string message) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Soundtrack playback failed: " + message)); StopSoundtrack(null); } private void HideHUD() { //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Expected O, but got Unknown if ((Object)(object)HUDManager.Instance == (Object)null) { return; } if (_hudHidden) { TakeoverBootstrap.Log.LogInfo((object)($"[TakeoverManager] HideHUD: already hidden ({_suppressedHUDGroups.Count} groups, " + $"{_addedHUDGroups.Count} added, {_hiddenHotbarObjects.Count} hotbar objects) — re-asserting.")); ReassertHiddenHUD(); return; } _hudHidden = true; try { _suppressedHUDGroups.Clear(); _addedHUDGroups.Clear(); object obj; if (!((Object)(object)HUDManager.Instance.HUDContainer != (Object)null)) { if (!((Object)(object)HUDManager.Instance.playerScreenTexture != (Object)null)) { obj = null; } else { Canvas canvas = ((Graphic)HUDManager.Instance.playerScreenTexture).canvas; obj = ((canvas != null) ? ((Component)canvas).transform : null); } } else { obj = HUDManager.Instance.HUDContainer.transform.parent; } Transform val = (Transform)obj; if ((Object)(object)val == (Object)null) { GameObject hUDContainer = HUDManager.Instance.HUDContainer; val = ((hUDContainer != null) ? hUDContainer.transform : null); } if ((Object)(object)val == (Object)null) { return; } CanvasGroup[] componentsInChildren = ((Component)val).GetComponentsInChildren(true); foreach (CanvasGroup val2 in componentsInChildren) { HudSuppressionRegistry.Suppress(val2, "monitor-takeover", (HUDElement)null); _suppressedHUDGroups.Add(val2); } Transform val3 = (((Object)(object)HUDManager.Instance.playerScreenTexture != (Object)null) ? ((Component)HUDManager.Instance.playerScreenTexture).transform : null); foreach (Transform item in val) { Transform val4 = item; if (!((Object)(object)val4 == (Object)null) && !((Object)(object)((Component)val4).GetComponent() != (Object)null)) { if ((Object)(object)val3 != (Object)null && IsAncestorOrSelf(val4, val3)) { TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] HideHUD: skipping '" + ((Object)val4).name + "' (ancestor of playerScreenTexture).")); continue; } CanvasGroup val5 = ((Component)val4).gameObject.AddComponent(); HudSuppressionRegistry.Suppress(val5, "monitor-takeover", (HUDElement)null); _addedHUDGroups.Add(val5); } } try { Image[] itemSlotIconFrames = HUDManager.Instance.itemSlotIconFrames; if (itemSlotIconFrames != null && itemSlotIconFrames.Length != 0) { Image[] array = itemSlotIconFrames; foreach (Image val6 in array) { if ((Object)(object)val6 == (Object)null) { continue; } Transform val7 = ((Component)val6).transform; while ((Object)(object)val7 != (Object)null && (Object)(object)((Component)val7).GetComponent() == (Object)null) { val7 = val7.parent; } if (!((Object)(object)val7 == (Object)null)) { if ((Object)(object)val7 == (Object)(object)val) { TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] HideHUD: hotbar canvas is the shared root — skipping to preserve player view."); } else { HideHUDAncestor(val7); } break; } } } } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] HideHUD hotbar: " + ex.Message)); } try { _hiddenHotbarObjects.Clear(); HUDManager instance = HUDManager.Instance; if (!((Object)(object)instance != (Object)null)) { return; } if (instance.itemSlotIcons != null) { Image[] array = instance.itemSlotIcons; foreach (Image val8 in array) { if (!((Object)(object)val8 == (Object)null)) { GameObject gameObject = ((Component)val8).gameObject; HudSuppressionRegistry.SuppressObject(gameObject, "monitor-takeover"); _hiddenHotbarObjects.Add(gameObject); } } } if (instance.itemSlotIconFrames != null) { Image[] array = instance.itemSlotIconFrames; foreach (Image val9 in array) { if (!((Object)(object)val9 == (Object)null)) { GameObject gameObject2 = ((Component)val9).gameObject; HudSuppressionRegistry.SuppressObject(gameObject2, "monitor-takeover"); _hiddenHotbarObjects.Add(gameObject2); } } } TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] HideHUD: hid {_hiddenHotbarObjects.Count} hotbar icon GameObjects."); } catch (Exception ex2) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] HideHUD hotbar icons: " + ex2.Message)); } } catch (Exception ex3) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] HideHUD: " + ex3.Message)); } } private void ReassertHiddenHUD() { try { foreach (CanvasGroup suppressedHUDGroup in _suppressedHUDGroups) { HudSuppressionRegistry.Suppress(suppressedHUDGroup, "monitor-takeover", (HUDElement)null); } foreach (CanvasGroup addedHUDGroup in _addedHUDGroups) { HudSuppressionRegistry.Suppress(addedHUDGroup, "monitor-takeover", (HUDElement)null); } foreach (GameObject hiddenHotbarObject in _hiddenHotbarObjects) { HudSuppressionRegistry.SuppressObject(hiddenHotbarObject, "monitor-takeover"); } } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] ReassertHiddenHUD: " + ex.Message)); } } private static bool IsAncestorOrSelf(Transform potentialAncestor, Transform t) { Transform val = t; while ((Object)(object)val != (Object)null) { if ((Object)(object)val == (Object)(object)potentialAncestor) { return true; } val = val.parent; } return false; } private static string GetTransformPath(Transform t) { if ((Object)(object)t == (Object)null) { return ""; } StringBuilder stringBuilder = new StringBuilder(((Object)t).name); Transform parent = t.parent; while ((Object)(object)parent != (Object)null) { stringBuilder.Insert(0, ((Object)parent).name + "/"); parent = parent.parent; } return stringBuilder.ToString(); } private void HideHUDAncestor(Transform t) { if ((Object)(object)t == (Object)null) { return; } CanvasGroup component = ((Component)t).GetComponent(); if ((Object)(object)component != (Object)null) { if (!_suppressedHUDGroups.Contains(component)) { HudSuppressionRegistry.Suppress(component, "monitor-takeover", (HUDElement)null); _suppressedHUDGroups.Add(component); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] HideHUD: hid existing CanvasGroup on '" + ((Object)t).name + "'.")); } } else { CanvasGroup val = ((Component)t).gameObject.AddComponent(); HudSuppressionRegistry.Suppress(val, "monitor-takeover", (HUDElement)null); _addedHUDGroups.Add(val); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] HideHUD: added CanvasGroup to '" + ((Object)t).name + "'.")); } } private void RestoreHUD() { if (!_hudHidden && _suppressedHUDGroups.Count == 0 && _addedHUDGroups.Count == 0 && _hiddenHotbarObjects.Count == 0) { return; } int num = 0; int num2 = 0; int num3 = 0; try { foreach (CanvasGroup suppressedHUDGroup in _suppressedHUDGroups) { if (HudSuppressionRegistry.Release(suppressedHUDGroup, "monitor-takeover")) { num++; } } _suppressedHUDGroups.Clear(); foreach (CanvasGroup addedHUDGroup in _addedHUDGroups) { if (!((Object)(object)addedHUDGroup == (Object)null)) { HudSuppressionRegistry.Release(addedHUDGroup, "monitor-takeover"); if (!HudSuppressionRegistry.IsSuppressed(addedHUDGroup)) { Object.Destroy((Object)(object)addedHUDGroup); num2++; } } } _addedHUDGroups.Clear(); foreach (GameObject hiddenHotbarObject in _hiddenHotbarObjects) { if (HudSuppressionRegistry.ReleaseObject(hiddenHotbarObject, "monitor-takeover")) { num3++; } } _hiddenHotbarObjects.Clear(); } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] RestoreHUD: " + ex.Message)); } finally { HudSuppressionRegistry.ReleaseAll("monitor-takeover"); HudSuppressionRegistry.ReleaseAllObjects("monitor-takeover"); _suppressedHUDGroups.Clear(); _addedHUDGroups.Clear(); _hiddenHotbarObjects.Clear(); _hudHidden = false; } TakeoverBootstrap.Log.LogInfo((object)($"[TakeoverManager] RestoreHUD: groups restored={num}, " + $"added groups destroyed={num2}, hotbar objects restored={num3}, " + $"still suppressed by others: groups={HudSuppressionRegistry.SuppressedGroupCount}, " + $"objects={HudSuppressionRegistry.SuppressedObjectCount}.")); } private void SetupVideo() { if (_useQuotaProgressionPayload) { QuotaTakeoverPayload currentPayload = QuotaProgressionRegistry.GetCurrentPayload(DialoguePool); if (QuotaProgressionRegistry.TryResolveMediaFile(currentPayload.MediaFile, currentPayload.MediaHash, out var path)) { if (Path.GetExtension(path).Equals(".mp4", StringComparison.OrdinalIgnoreCase)) { SetupVideoForUrl(path, QuotaProgressionRegistry.LoopMedia); return; } if (SetupImage(path)) { return; } } } SetupDefaultVideo(allowLooseFile: true); } private void SetupDefaultVideo(bool allowLooseFile) { string text = (allowLooseFile ? TakeoverBootstrap.TakeoverVideoPath : null); if (!string.IsNullOrEmpty(text)) { _looseDefaultVideoInUse = true; SetupVideoForUrl(text, looping: true, configuredSource: false); return; } _looseDefaultVideoInUse = false; if ((Object)(object)TakeoverBootstrap.TakeoverVideoClip == (Object)null) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] No takeover video available: neither the loose mp4 nor the bundled clip resolved. The sequence still runs; the monitors will carry CRT static."); EnsureRenderTexture(); } else { SetupVideoFor(TakeoverBootstrap.TakeoverVideoClip, looping: true); } } private void EnsureRenderTexture() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_renderTexture != (Object)null)) { _renderTexture = CreateMonitorRenderTexture(); TakeoverBootstrap.Log.LogInfo((object)($"[Diag/RT] constructed: id={((Object)_renderTexture).GetInstanceID()} " + $"size={((Texture)_renderTexture).width}x{((Texture)_renderTexture).height} fmt={_renderTexture.format} name='{((Object)_renderTexture).name}'")); } } private RenderTexture CreateMonitorRenderTexture() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) RenderTexture val = new RenderTexture(1920, 1080, 0, (RenderTextureFormat)0); val.Create(); RenderTexture active = RenderTexture.active; try { RenderTexture.active = val; GL.Clear(true, true, Color.black); } catch { } finally { RenderTexture.active = active; } return val; } private void SetupVideoForUrl(string path, bool looping, bool configuredSource = true) { EnsureRenderTexture(); _usingConfiguredVideoSource = configuredSource; _videoPlayer = ((Component)this).gameObject.AddComponent(); _videoPlayer.source = (VideoSource)1; _videoPlayer.url = path; _videoPlayer.renderMode = (VideoRenderMode)2; _videoPlayer.targetTexture = _renderTexture; _videoPlayer.isLooping = looping; if (configuredSource && _soundtrackPlan.Kind == TakeoverSoundtrackKind.ReuseVideoTrack) { _videoPlayer.audioOutputMode = (VideoAudioOutputMode)1; _videoPlayer.controlledAudioTrackCount = 1; _videoPlayer.SetTargetAudioSource((ushort)0, EnsureSoundtrackSource()); } else { _videoPlayer.audioOutputMode = (VideoAudioOutputMode)((configuredSource && QuotaProgressionRegistry.EnableMediaAudio && !_soundtrackPlan.Active) ? 2 : 0); } _videoPlayer.playOnAwake = false; TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] Preparing " + (configuredSource ? "configured" : "default") + " MP4 '" + Path.GetFileName(path) + "' through VideoPlayer.url.")); HardenAndPrepare(_videoPlayer); } private bool SetupImage(string path) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown try { byte[] array = File.ReadAllBytes(path); if (array.Length > 33554432) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] Configured image exceeds the 32 MB limit; using bundled video."); return false; } _customImageTexture = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(_customImageTexture, array, false)) { return false; } ((Object)_customImageTexture).name = "Y4NGZ_CustomTakeoverImage"; EnsureRenderTexture(); Graphics.Blit((Texture)(object)_customImageTexture, _renderTexture); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] Loaded configured image '" + Path.GetFileName(path) + "'.")); return true; } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Configured image failed: " + ex.Message)); return false; } } private void SetupVideoFor(VideoClip clip, bool looping) { if (!((Object)(object)clip == (Object)null)) { EnsureRenderTexture(); _usingConfiguredVideoSource = false; _videoPlayer = ((Component)this).gameObject.AddComponent(); _videoPlayer.clip = clip; _videoPlayer.renderMode = (VideoRenderMode)2; _videoPlayer.targetTexture = _renderTexture; _videoPlayer.isLooping = looping; _videoPlayer.audioOutputMode = (VideoAudioOutputMode)0; _videoPlayer.playOnAwake = false; TakeoverBootstrap.Log.LogInfo((object)("[Diag/RT] VP.targetTexture id=" + (((Object)(object)_videoPlayer.targetTexture != (Object)null) ? ((Object)_videoPlayer.targetTexture).GetInstanceID().ToString() : "NULL") + " " + string.Format("match={0} clip='{1}'", (Object)(object)_videoPlayer.targetTexture == (Object)(object)_renderTexture, ((Object)(object)_videoPlayer.clip != (Object)null) ? ((Object)_videoPlayer.clip).name : "null"))); HardenAndPrepare(_videoPlayer); } } private void HardenAndPrepare(VideoPlayer player) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown if ((Object)(object)player == (Object)null) { return; } try { if ((int)player.audioOutputMode == 0) { ushort audioTrackCount = player.audioTrackCount; for (ushort num = 0; num < audioTrackCount; num++) { player.EnableAudioTrack(num, false); } player.controlledAudioTrackCount = 0; } player.skipOnDrop = true; player.playbackSpeed = 1f; player.timeReference = (VideoTimeReference)0; player.waitForFirstFrame = false; player.errorReceived -= new ErrorEventHandler(OnVideoErrorReceived); player.errorReceived += new ErrorEventHandler(OnVideoErrorReceived); player.prepareCompleted -= new EventHandler(OnVideoPrepareCompleted); player.prepareCompleted += new EventHandler(OnVideoPrepareCompleted); } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Video hardening failed: " + ex.Message)); } LogVideoCarrierState("setup"); IssuePrepare(player, "initial"); } private void IssuePrepare(VideoPlayer player, string reason) { if ((Object)(object)player == (Object)null) { return; } try { if (!((Component)player).gameObject.activeInHierarchy) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Video cannot prepare: the carrier GameObject '" + ((Object)((Component)player).gameObject).name + "' is inactive. Reactivating it.")); ((Component)player).gameObject.SetActive(true); } if (!((Behaviour)player).enabled) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] Video cannot prepare: the VideoPlayer component was disabled. Re-enabling it."); ((Behaviour)player).enabled = true; } player.Prepare(); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] Video Prepare() issued (" + reason + ").")); } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Video Prepare() (" + reason + ") threw: " + ex.Message)); } } private void OnVideoErrorReceived(VideoPlayer source, string message) { TakeoverBootstrap.Log.LogError((object)("[TakeoverManager] VideoPlayer error: " + message + " (clip='" + (((Object)(object)source != (Object)null && (Object)(object)source.clip != (Object)null) ? ((Object)source.clip).name : "null") + "' url='" + (((Object)(object)source != (Object)null) ? source.url : string.Empty) + "'). This is why the takeover has no picture; the static fallback will carry the sequence.")); } private void OnVideoPrepareCompleted(VideoPlayer source) { TakeoverBootstrap.Log.LogInfo((object)($"[TakeoverManager] Video prepared: {((source != null) ? new uint?(source.width) : ((uint?)null))}x{((source != null) ? new uint?(source.height) : ((uint?)null))} " + $"frames={((source != null) ? new ulong?(source.frameCount) : ((ulong?)null))} audioTracks={((source != null) ? new ushort?(source.audioTrackCount) : ((ushort?)null))}.")); } private void LogVideoCarrierState(string site) { //IL_0077: 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_0094: 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_00da: Unknown result type (might be due to invalid IL or missing references) VideoPlayer videoPlayer = _videoPlayer; if ((Object)(object)videoPlayer == (Object)null) { TakeoverBootstrap.Log.LogInfo((object)("[Diag/Video] carrier (" + site + "): no VideoPlayer.")); return; } GameObject gameObject = ((Component)videoPlayer).gameObject; VideoClip clip = videoPlayer.clip; ManualLogSource log = TakeoverBootstrap.Log; string[] obj = new string[12] { $"[Diag/Video] carrier ({site}): go='{((Object)gameObject).name}' activeSelf={gameObject.activeSelf} ", $"activeInHierarchy={gameObject.activeInHierarchy} hideFlags={((Object)gameObject).hideFlags} ", null, null, null, null, null, null, null, null, null, null }; Scene scene = gameObject.scene; obj[2] = $"scene='{((Scene)(ref scene)).name}' componentEnabled={((Behaviour)videoPlayer).enabled} "; obj[3] = $"players-on-carrier={gameObject.GetComponents().Length} "; obj[4] = $"source={videoPlayer.source} audioOut={videoPlayer.audioOutputMode} "; obj[5] = $"audioTracks={videoPlayer.audioTrackCount} controlledTracks={videoPlayer.controlledAudioTrackCount} "; obj[6] = string.Format("clip='{0}' clipLen={1:F2} ", ((Object)(object)clip != (Object)null) ? ((Object)clip).name : "null", ((Object)(object)clip != (Object)null) ? clip.length : 0.0); obj[7] = $"clipSize={(((Object)(object)clip != (Object)null) ? clip.width : 0u)}x{(((Object)(object)clip != (Object)null) ? clip.height : 0u)} "; obj[8] = $"clipFps={(((Object)(object)clip != (Object)null) ? clip.frameRate : 0.0):F2} "; obj[9] = "clipPath='"; obj[10] = (((Object)(object)clip != (Object)null) ? clip.originalPath : string.Empty); obj[11] = "'"; log.LogInfo((object)string.Concat(obj)); } private IEnumerator PrimeVideoForDisplay() { if (!((Object)(object)_videoPlayer == (Object)null)) { float elapsed = 0f; while (!_videoPlayer.isPrepared && elapsed < 1f) { elapsed += Time.deltaTime; yield return null; } bool isPrepared = _videoPlayer.isPrepared; _videoPlayer.Play(); if (isPrepared) { yield return (object)new WaitForEndOfFrame(); } else { TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] Video is still decoding at monitor-handoff time; the sequence continues and the picture appears as soon as the first frame lands."); } if (_videoFallbackCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_videoFallbackCoroutine); } _videoFallbackCoroutine = ((MonoBehaviour)this).StartCoroutine(WatchVideoDecode(elapsed)); } } private IEnumerator AdoptConfiguredMediaLength() { _mediaLengthPending = true; _mediaLengthSeconds = 0.0; float waited = 0f; while (_takeoverActive && _usingConfiguredVideoSource && (Object)(object)_videoPlayer != (Object)null && !_videoPlayer.isPrepared && waited < 30f) { waited += Time.deltaTime; yield return null; } if (_takeoverActive && _usingConfiguredVideoSource && (Object)(object)_videoPlayer != (Object)null && _videoPlayer.isPrepared && _videoPlayer.length > 0.5) { _mediaLengthSeconds = Mathf.Clamp((float)_videoPlayer.length, 3f, 1800f); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] UseMediaLengthAsDuration: holding for the configured video's " + $"{_mediaLengthSeconds:F1}s (reported {_videoPlayer.length:F1}s after {waited:F1}s of prepare).")); } else { TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] UseMediaLengthAsDuration: no usable length from the configured video after " + string.Format("{0:F1}s ({1}); ", waited, _usingConfiguredVideoSource ? "still the configured source" : "fell back to the default video") + "keeping the configured Takeover Duration.")); } _mediaLengthPending = false; } private IEnumerator WatchVideoDecode(float alreadyWaited) { float elapsed = alreadyWaited; float nextRetryAt = elapsed + 2f; bool swapped = false; while (_takeoverActive && elapsed < 6f) { if ((Object)(object)_videoPlayer != (Object)null && _videoPlayer.isPrepared) { if (!_videoPlayer.isPlaying) { _videoPlayer.Play(); } StopSignalLostStatic("the video decoded"); _videoFallbackCoroutine = null; yield break; } if (!swapped && _usingConfiguredVideoSource && elapsed >= 4f) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] Configured video did not prepare in time; using the default takeover video."); SwapToDefaultVideo(); swapped = true; nextRetryAt = elapsed + 2f; } else if (elapsed >= nextRetryAt) { LogVideoCarrierState("retry"); IssuePrepare(_videoPlayer, "watchdog retry"); if ((Object)(object)_videoPlayer != (Object)null && !_videoPlayer.isPlaying) { _videoPlayer.Play(); } nextRetryAt = elapsed + 2f; } elapsed += Time.deltaTime; yield return null; } _videoFallbackCoroutine = null; if (_takeoverActive && (!((Object)(object)_videoPlayer != (Object)null) || !_videoPlayer.isPrepared)) { LogVideoCarrierState("signal-lost"); TakeoverBootstrap.Log.LogError((object)($"[TakeoverManager] No video frame decoded within {6f:F0}s. The monitors " + "are switching to CRT static so the takeover is not a black wall. If a 'VideoPlayer error' line appears above, that is the authoritative reason; if none does, the decoder accepted the media and never produced a frame.")); StartSignalLostStatic(); } } private void SetupAdditionalMediaPlayers(TakeoverMediaPlan plan, bool looping) { if (plan.Active) { int num = Math.Min(Math.Max(0, plan.MaxPlayers - 1), plan.Sources.Count); for (int i = 0; i < num; i++) { CreateMediaPlayer(i + 1, plan.Sources[i], looping); } TakeoverBootstrap.Log.LogInfo((object)($"[TakeoverManager] Per-monitor media: opened {_extraMediaPlayers.Count} extra player(s) " + $"from a pool of {plan.Sources.Count} resolved source(s) under a cap of {plan.MaxPlayers}, " + $"seed {plan.Seed}.")); } } private void CreateMediaPlayer(int index, string source, bool looping) { TakeoverExtraMediaPlayer takeoverExtraMediaPlayer = new TakeoverExtraMediaPlayer { Index = index, Source = source, Texture = CreateMonitorRenderTexture() }; ((Object)takeoverExtraMediaPlayer.Texture).name = $"Y4NGZ_TakeoverMedia{index}"; _extraMediaPlayers.Add(takeoverExtraMediaPlayer); if (!Path.GetExtension(source ?? string.Empty).Equals(".mp4", StringComparison.OrdinalIgnoreCase)) { if (!TryBlitImageInto(source, takeoverExtraMediaPlayer)) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Per-monitor media source '" + Path.GetFileName(source) + "' could not be loaded as an image; that surface mirrors the primary instead.")); takeoverExtraMediaPlayer.MirrorLoop = ((MonoBehaviour)this).StartCoroutine(MirrorPrimaryLoop(takeoverExtraMediaPlayer)); } return; } VideoPlayer val = ((Component)this).gameObject.AddComponent(); val.source = (VideoSource)1; val.url = source; val.renderMode = (VideoRenderMode)2; val.targetTexture = takeoverExtraMediaPlayer.Texture; val.isLooping = looping; val.audioOutputMode = (VideoAudioOutputMode)0; val.playOnAwake = false; takeoverExtraMediaPlayer.Player = val; HardenAndPrepare(val); val.Play(); takeoverExtraMediaPlayer.Watchdog = ((MonoBehaviour)this).StartCoroutine(WatchMediaPlayerDecode(takeoverExtraMediaPlayer)); } private bool TryBlitImageInto(string path, TakeoverExtraMediaPlayer entry) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown try { byte[] array = File.ReadAllBytes(path); if (array.Length > 33554432) { return false; } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, array, false)) { Object.Destroy((Object)(object)val); return false; } ((Object)val).name = $"Y4NGZ_TakeoverMediaImage{entry.Index}"; entry.ImageTexture = val; Graphics.Blit((Texture)(object)val, entry.Texture); return true; } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Per-monitor media image load failed: " + ex.Message)); return false; } } private IEnumerator WatchMediaPlayerDecode(TakeoverExtraMediaPlayer entry) { float elapsed = 0f; while (_takeoverActive && elapsed < 4f) { if ((Object)(object)entry.Player == (Object)null) { entry.Watchdog = null; yield break; } if (entry.Player.isPrepared) { if (!entry.Player.isPlaying) { entry.Player.Play(); } entry.Watchdog = null; yield break; } elapsed += Time.deltaTime; yield return null; } entry.Watchdog = null; if (!_takeoverActive || ((Object)(object)entry.Player != (Object)null && entry.Player.isPrepared)) { yield break; } TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Per-monitor media source '" + Path.GetFileName(entry.Source) + "' " + $"did not decode within {4f:F0}s; its surfaces mirror the " + "primary video for the rest of the takeover.")); if ((Object)(object)entry.Player != (Object)null) { try { entry.Player.Stop(); } catch { } entry.Player.errorReceived -= new ErrorEventHandler(OnVideoErrorReceived); entry.Player.prepareCompleted -= new EventHandler(OnVideoPrepareCompleted); Object.Destroy((Object)(object)entry.Player); entry.Player = null; } entry.MirrorLoop = ((MonoBehaviour)this).StartCoroutine(MirrorPrimaryLoop(entry)); } private IEnumerator MirrorPrimaryLoop(TakeoverExtraMediaPlayer entry) { while (_takeoverActive && (Object)(object)entry.Texture != (Object)null && (Object)(object)_renderTexture != (Object)null) { try { Graphics.Blit((Texture)(object)_renderTexture, entry.Texture); } catch { } yield return null; } entry.MirrorLoop = null; } private void StopAdditionalMediaPlayback() { foreach (TakeoverExtraMediaPlayer extraMediaPlayer in _extraMediaPlayers) { if (extraMediaPlayer == null) { continue; } if (extraMediaPlayer.Watchdog != null) { ((MonoBehaviour)this).StopCoroutine(extraMediaPlayer.Watchdog); extraMediaPlayer.Watchdog = null; } if (extraMediaPlayer.MirrorLoop != null) { ((MonoBehaviour)this).StopCoroutine(extraMediaPlayer.MirrorLoop); extraMediaPlayer.MirrorLoop = null; } if ((Object)(object)extraMediaPlayer.Player != (Object)null) { try { extraMediaPlayer.Player.Stop(); } catch { } } } } private void DestroyAdditionalMediaPlayers() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown foreach (TakeoverExtraMediaPlayer extraMediaPlayer in _extraMediaPlayers) { if (extraMediaPlayer == null) { continue; } if (extraMediaPlayer.Watchdog != null) { ((MonoBehaviour)this).StopCoroutine(extraMediaPlayer.Watchdog); extraMediaPlayer.Watchdog = null; } if (extraMediaPlayer.MirrorLoop != null) { ((MonoBehaviour)this).StopCoroutine(extraMediaPlayer.MirrorLoop); extraMediaPlayer.MirrorLoop = null; } if ((Object)(object)extraMediaPlayer.Player != (Object)null) { try { extraMediaPlayer.Player.Stop(); } catch { } extraMediaPlayer.Player.errorReceived -= new ErrorEventHandler(OnVideoErrorReceived); extraMediaPlayer.Player.prepareCompleted -= new EventHandler(OnVideoPrepareCompleted); Object.Destroy((Object)(object)extraMediaPlayer.Player); extraMediaPlayer.Player = null; } if ((Object)(object)extraMediaPlayer.Texture != (Object)null) { extraMediaPlayer.Texture.Release(); Object.Destroy((Object)(object)extraMediaPlayer.Texture); extraMediaPlayer.Texture = null; } if ((Object)(object)extraMediaPlayer.ImageTexture != (Object)null) { Object.Destroy((Object)(object)extraMediaPlayer.ImageTexture); extraMediaPlayer.ImageTexture = null; } } _extraMediaPlayers.Clear(); _mediaPaintSites.Clear(); _mediaPlan = default(TakeoverMediaPlan); } private Texture MediaTextureFor(int playerIndex) { if (playerIndex <= 0) { return (Texture)(object)_renderTexture; } int num = playerIndex - 1; if (num >= _extraMediaPlayers.Count) { return (Texture)(object)_renderTexture; } if (!((Object)(object)_extraMediaPlayers[num].Texture != (Object)null)) { return (Texture)(object)_renderTexture; } return (Texture)(object)_extraMediaPlayers[num].Texture; } private void SwapToDefaultVideo() { bool flag = !_looseDefaultVideoInUse; if (!flag && (Object)(object)TakeoverBootstrap.TakeoverVideoClip == (Object)null) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] No default takeover video to fall back to; leaving the current source in place."); return; } if ((Object)(object)_videoPlayer != (Object)null) { try { _videoPlayer.Stop(); } catch { } Object.Destroy((Object)(object)_videoPlayer); _videoPlayer = null; } SetupDefaultVideo(flag); VideoPlayer videoPlayer = _videoPlayer; if (videoPlayer != null) { videoPlayer.Play(); } } private void StartSignalLostStatic() { if (_signalLostCoroutine == null && !((Object)(object)_renderTexture == (Object)null)) { _signalLostCoroutine = ((MonoBehaviour)this).StartCoroutine(SignalLostStaticLoop()); } } private void StopSignalLostStatic(string reason) { if (_signalLostCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_signalLostCoroutine); _signalLostCoroutine = null; DestroySignalLostFrames(); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] CRT static fallback stopped: " + reason + ".")); } } private void DestroySignalLostFrames() { if (_signalLostFrames == null) { return; } for (int i = 0; i < _signalLostFrames.Length; i++) { if ((Object)(object)_signalLostFrames[i] != (Object)null) { Object.Destroy((Object)(object)_signalLostFrames[i]); } } _signalLostFrames = null; } private IEnumerator SignalLostStaticLoop() { _signalLostFrames = (Texture2D[])(object)new Texture2D[6]; for (int i = 0; i < 6; i++) { _signalLostFrames[i] = CRTStatic.CreateNoiseTexture(320, 180); } WaitForSeconds wait = new WaitForSeconds(0.05f); int index = 0; while (_takeoverActive && (Object)(object)_renderTexture != (Object)null) { if ((Object)(object)_videoPlayer != (Object)null && _videoPlayer.isPrepared) { _signalLostCoroutine = null; DestroySignalLostFrames(); TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] CRT static fallback stopped: the video decoded late."); yield break; } Texture2D val = _signalLostFrames[index]; index = (index + 1) % 6; if ((Object)(object)val != (Object)null) { try { Graphics.Blit((Texture)(object)val, _renderTexture); } catch { } } yield return wait; } _signalLostCoroutine = null; DestroySignalLostFrames(); } private void OverrideMonitors() { //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_0915: Unknown result type (might be due to invalid IL or missing references) //IL_091f: Expected O, but got Unknown QuotaUnlockAnnouncement.AbortForExternalTakeover(); if (_generalImprovementsLeaseCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_generalImprovementsLeaseCoroutine); _generalImprovementsLeaseCoroutine = null; } GeneralImprovementsMonitorLease generalImprovementsMonitorLease = _generalImprovementsMonitorLease; if (generalImprovementsMonitorLease != null) { generalImprovementsMonitorLease.Release(); } _generalImprovementsMonitorLease = null; _overriddenRenderers.Clear(); _savedHDRPState.Clear(); _topMonitorOverlayImages.Clear(); _disabledComponents.Clear(); _disabledMonitorGraphics.Clear(); _mediaPaintSites.Clear(); _videoReelRawImage = null; StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)_renderTexture == (Object)null) { return; } bool betterMonitorsActive = GeneralImprovementsMonitors.BetterMonitorsActive; if (betterMonitorsActive) { GeneralImprovementsMonitors.LogStandDown(TakeoverBootstrap.Log, "the takeover's top-monitor canvas overlay", "quota/deadline are rendered into GI's screen materials, not the world-space canvas; the GI screen pass paints those monitors instead"); } else { try { Image val = ResolveVisibleMonitorGraphic(instance, instance.profitQuotaMonitorBGImage, "ProfitQuotaBG"); Image val2 = ResolveVisibleMonitorGraphic(instance, instance.deadlineMonitorBGImage, "DeadlineBG"); TextMeshProUGUI graphic = ResolveVisibleMonitorGraphic(instance, instance.profitQuotaMonitorText, "ProfitQuotaText"); TextMeshProUGUI graphic2 = ResolveVisibleMonitorGraphic(instance, instance.deadlineMonitorText, "DeadlineText"); DisableMonitorGraphic((Behaviour)(object)instance.profitQuotaMonitorText); DisableMonitorGraphic((Behaviour)(object)instance.profitQuotaMonitorBGImage); DisableMonitorGraphic((Behaviour)(object)instance.deadlineMonitorText); DisableMonitorGraphic((Behaviour)(object)instance.deadlineMonitorBGImage); DisableMonitorGraphic((Behaviour)(object)graphic); DisableMonitorGraphic((Behaviour)(object)val); DisableMonitorGraphic((Behaviour)(object)graphic2); DisableMonitorGraphic((Behaviour)(object)val2); _topMonitorOverlayImages.Clear(); AddOverlayMatchingBG(val); AddOverlayMatchingBG(val2); if ((Object)(object)val != (Object)(object)instance.profitQuotaMonitorBGImage) { AddOverlayMatchingBG(instance.profitQuotaMonitorBGImage); } if ((Object)(object)val2 != (Object)(object)instance.deadlineMonitorBGImage) { AddOverlayMatchingBG(instance.deadlineMonitorBGImage); } TakeoverBootstrap.Log.LogInfo((object)($"[TakeoverManager] Top monitor overlays added: {_topMonitorOverlayImages.Count} " + "(profit='" + (((Object)(object)val != (Object)null) ? ((Object)val).name : "") + "', deadline='" + (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "") + "', " + $"graphics disabled: {_disabledMonitorGraphics.Count}).")); } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Top monitor override: " + ex.Message)); } } try { _levelDescWasEnabled = SetEnabled((Behaviour)(object)instance.screenLevelDescription, enabled: false); if ((Object)(object)instance.screenLevelVideoReel != (Object)null) { VideoPlayer screenLevelVideoReel = instance.screenLevelVideoReel; _savedVideoClip = screenLevelVideoReel.clip; _savedVideoLooping = screenLevelVideoReel.isLooping; _savedVideoPlaying = screenLevelVideoReel.isPlaying; TakeoverBootstrap.Log.LogInfo((object)($"[Diag/Video] vanilla screenLevelVideoReel: isPrepared={screenLevelVideoReel.isPrepared} " + $"isPlaying={screenLevelVideoReel.isPlaying} frame={screenLevelVideoReel.frame}/{screenLevelVideoReel.frameCount} " + "clip='" + (((Object)(object)screenLevelVideoReel.clip != (Object)null) ? ((Object)screenLevelVideoReel.clip).name : "null") + "' " + $"audioOut={screenLevelVideoReel.audioOutputMode} enabled={((Behaviour)screenLevelVideoReel).enabled} " + $"activeInHierarchy={((Component)screenLevelVideoReel).gameObject.activeInHierarchy}")); _mapScreenRT = screenLevelVideoReel.targetTexture; screenLevelVideoReel.Stop(); } if ((Object)(object)_mapScreenRT != (Object)null && (Object)(object)_renderTexture != (Object)null) { _mapBlitCoroutine = ((MonoBehaviour)this).StartCoroutine(BlitMapScreenLoop()); TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] Pinning MapScreenVideo RT ({((Texture)_mapScreenRT).width}x{((Texture)_mapScreenRT).height}) to Y4NGZ via per-frame Blit."); } GameObject val3 = GameObject.Find("Systems/GameSystems/ItemSystems/MapScreenUI"); if ((Object)(object)val3 != (Object)null) { RawImage[] componentsInChildren = val3.GetComponentsInChildren(true); foreach (RawImage val4 in componentsInChildren) { if ((Object)(object)val4.texture != (Object)null && ((Object)val4.texture).name == "MapScreenVideo") { _videoReelRawImage = val4; _savedVideoReelTexture = val4.texture; val4.texture = (Texture)(object)_renderTexture; break; } } } } catch (Exception ex2) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Bottom-left override: " + ex2.Message)); } try { GameObject val5 = GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall"); if ((Object)(object)val5 != (Object)null) { TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] --- MonitorWall hierarchy dump ---"); DumpHierarchy(val5.transform, 0); TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] --- end MonitorWall dump ---"); } else { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] MonitorWall not found for hierarchy dump."); } } catch (Exception ex3) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] MonitorWall dump: " + ex3.Message)); } try { MeshRenderer val6 = (betterMonitorsActive ? GeneralImprovementsMonitors.ResolveMapScreenSurface(instance?.mapScreen) : null); GameObject val7 = ((!betterMonitorsActive) ? GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall/Cube.001") : (((Object)(object)val6 != (Object)null) ? ((Component)val6).gameObject : null)); if (betterMonitorsActive && (Object)(object)val7 == (Object)null) { GeneralImprovementsMonitors.LogStandDown(TakeoverBootstrap.Log, "the takeover's map-screen override", "GI's replacement map surface could not be resolved from StartOfRound.mapScreen"); } if ((Object)(object)val7 != (Object)null) { MeshRenderer val8 = val7.GetComponent(); if ((Object)(object)val8 != (Object)null && GeneralImprovementsMonitors.IsHiddenByBetterMonitors((Renderer)(object)val8)) { GeneralImprovementsMonitors.LogStandDown(TakeoverBootstrap.Log, "the takeover's map-screen override on '" + ((Object)((Component)val8).gameObject).name + "'", "GI disabled that renderer, so painting it would draw nothing"); val8 = null; } if ((Object)(object)val8 != (Object)null && !AlreadyOverridden(val8)) { if ((Object)(object)instance != (Object)null && (Object)(object)instance.mapScreen != (Object)null) { _disabledComponents.Add(((Behaviour)(object)instance.mapScreen, ((Behaviour)instance.mapScreen).enabled)); ((Behaviour)instance.mapScreen).enabled = false; TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] Cube.001: disabled sor.mapScreen MCR on '" + ((Object)((Component)instance.mapScreen).gameObject).name + "'.")); } else { TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] Cube.001: sor.mapScreen null — relying on global MCR disable."); } if (betterMonitorsActive) { TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] GeneralImprovements map surface '" + GetHierarchyPath(((Component)val8).transform) + "' delegated to the wall property-block lease.")); } else { Texture item = (((Object)(object)((Renderer)val8).sharedMaterial != (Object)null) ? ((Renderer)val8).sharedMaterial.mainTexture : null); _overriddenRenderers.Add((val8, item)); ApplyMonitorOverride(val8); TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] Cube.001 overridden via HDRP write."); } } } else if (!betterMonitorsActive) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] Cube.001 not found."); } } catch (Exception ex4) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Bottom-left override: " + ex4.Message)); } try { GameObject val9 = GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall/SingleScreen"); if ((Object)(object)val9 != (Object)null) { MeshRenderer val10 = val9.GetComponent(); if ((Object)(object)val10 != (Object)null && GeneralImprovementsMonitors.IsHiddenByBetterMonitors((Renderer)(object)val10)) { GeneralImprovementsMonitors.LogStandDown(TakeoverBootstrap.Log, "the takeover's SingleScreen override", "GI disabled that renderer, so painting it would draw nothing"); val10 = null; } if ((Object)(object)val10 != (Object)null && !AlreadyOverridden(val10)) { Texture item2 = (((Object)(object)((Renderer)val10).sharedMaterial != (Object)null) ? ((Renderer)val10).sharedMaterial.mainTexture : null); _overriddenRenderers.Add((val10, item2)); ApplyMonitorOverride(val10); TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] SingleScreen overridden."); } } else { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] SingleScreen not found."); } } catch (Exception ex5) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] SingleScreen override: " + ex5.Message)); } try { GameObject val11 = GameObject.Find("Environment/HangarShip/ShipModels2b/MonitorWall"); if ((Object)(object)val11 != (Object)null && TakeoverBootstrap.CfgOverrideFifthMonitor.Value) { MeshRenderer[] componentsInChildren2 = val11.GetComponentsInChildren(true); foreach (MeshRenderer val12 in componentsInChildren2) { if (!((Object)(object)val12 == (Object)null) && !AlreadyOverridden(val12) && !GeneralImprovementsMonitors.IsHiddenByBetterMonitors((Renderer)(object)val12) && (!betterMonitorsActive || !IsUnderGeneralImprovementsMonitorGroup(((Component)val12).transform)) && IsMonitorScreen(val12) && MatchesAdditionalMonitorFilter(val12)) { try { Texture item3 = (((Object)(object)((Renderer)val12).sharedMaterial != (Object)null) ? ((Renderer)val12).sharedMaterial.mainTexture : null); _overriddenRenderers.Add((val12, item3)); ApplyMonitorOverride(val12); TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] Fifth monitor override: " + GetHierarchyPath(((Component)val12).transform))); } catch { } } } } else if ((Object)(object)val11 != (Object)null) { TakeoverBootstrap.Log.LogInfo((object)"[TakeoverManager] Fifth-monitor pass skipped (CfgOverrideFifthMonitor=false)."); } if (betterMonitorsActive) { _generalImprovementsMonitorLease = new GeneralImprovementsMonitorLease((SurfaceSet)0); IReadOnlyList readOnlyList = BuildGiSurfaceTextures(); bool flag; string detail = default(string); if (readOnlyList != null) { try { flag = AcquireGiLeasePerSurface(readOnlyList, out detail); } catch (MissingMethodException) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] This profile's Y4NGZCore predates the per-surface monitor lease (1.0.6); the GeneralImprovements wall shows the primary source on every screen for this takeover."); flag = _generalImprovementsMonitorLease.Acquire((Texture)(object)_renderTexture, ref detail); } } else { flag = _generalImprovementsMonitorLease.Acquire((Texture)(object)_renderTexture, ref detail); } _generalImprovementsLeaseCoroutine = ((MonoBehaviour)this).StartCoroutine(MaintainGeneralImprovementsMonitorLease()); if (flag) { TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] " + detail + ".")); } else { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] " + detail + "; maintenance will bind the rebuilt wall when it appears.")); } } } catch (Exception ex7) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] MonitorWall/RT scan: " + ex7.Message)); } try { int num = 0; ManualCameraRenderer[] array = Object.FindObjectsOfType(); foreach (ManualCameraRenderer val13 in array) { if (!((Object)(object)val13 == (Object)null)) { if (AlreadyDisabled((Behaviour)(object)val13)) { ((Behaviour)val13).enabled = false; continue; } _disabledComponents.Add(((Behaviour)(object)val13, ((Behaviour)val13).enabled)); ((Behaviour)val13).enabled = false; num++; } } TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] Disabled {_disabledComponents.Count} ManualCameraRenderer components (+{num} this pass)."); } catch (Exception ex8) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] MCR disable: " + ex8.Message)); } try { List list = new List(); foreach (var overriddenRenderer in _overriddenRenderers) { MeshRenderer item4 = overriddenRenderer.renderer; if ((Object)(object)item4 != (Object)null) { list.Add(((Object)((Component)item4).gameObject).name); } } TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] Override summary: " + string.Join(", ", list))); } catch { } AssignPerMonitorMedia(); TakeoverBootstrap.Log.LogInfo((object)($"[TakeoverManager] Override complete. MeshRenderers: {_overriddenRenderers.Count}, " + $"Canvas overlays: {_topMonitorOverlayImages.Count}, " + "VideoReel RawImage: " + (((Object)(object)_videoReelRawImage != (Object)null) ? "found" : "missing"))); ((MonoBehaviour)this).StartCoroutine(VerifyOverridesAfterDelay()); } private IEnumerator MaintainGeneralImprovementsMonitorLease() { while (_takeoverActive) { GeneralImprovementsMonitorLease generalImprovementsMonitorLease = _generalImprovementsMonitorLease; if (generalImprovementsMonitorLease == null || !generalImprovementsMonitorLease.IsActive) { break; } _generalImprovementsMonitorLease.Maintain(false); yield return GiLeaseRefresh; } _generalImprovementsLeaseCoroutine = null; } [MethodImpl(MethodImplOptions.NoInlining)] private bool AcquireGiLeasePerSurface(IReadOnlyList textures, out string detail) { return _generalImprovementsMonitorLease.Acquire((Texture)(object)_renderTexture, textures, ref detail); } private IReadOnlyList BuildGiSurfaceTextures() { int activeMediaPlayerCount = ActiveMediaPlayerCount; if (!_mediaPlan.Active || activeMediaPlayerCount < 2) { return null; } int[] permutation = TakeoverMediaAssignment.BuildPermutation(_mediaPlan.Seed, activeMediaPlayerCount); List list = new List(32) { (Texture)(object)_renderTexture }; for (int i = 1; i < 32; i++) { Texture val = MediaTextureFor(TakeoverMediaAssignment.PlayerForSurface(i, activeMediaPlayerCount, permutation)); list.Add((Texture)(((Object)(object)val != (Object)null) ? ((object)val) : ((object)_renderTexture))); } return list; } private void AssignPerMonitorMedia() { if (!_mediaPlan.Active || _mediaPaintSites.Count == 0) { return; } List list = new List(_mediaPaintSites.Count); foreach (TakeoverMediaPaintSite mediaPaintSite in _mediaPaintSites) { list.Add(mediaPaintSite.Key); } TakeoverMediaAssignment.SortDeterministically(list); int num = TakeoverMediaAssignment.ClampPlayerCount(ActiveMediaPlayerCount, _mediaPlan.Sources.Count, list.Count); if (num < 2) { return; } int[] permutation = TakeoverMediaAssignment.BuildPermutation(_mediaPlan.Seed, num); int num2 = 0; foreach (TakeoverMediaPaintSite mediaPaintSite2 in _mediaPaintSites) { int num3 = TakeoverMediaAssignment.PlayerForSurface(list.IndexOf(mediaPaintSite2.Key), num, permutation); if (num3 == 0) { continue; } Texture val = MediaTextureFor(num3); if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)_renderTexture)) { if ((Object)(object)mediaPaintSite2.Overlay != (Object)null) { mediaPaintSite2.Overlay.texture = val; } else { PaintScreenMaterial(mediaPaintSite2.Material, val); } num2++; } } TakeoverBootstrap.Log.LogInfo((object)($"[TakeoverManager] Per-monitor media: {_mediaPaintSites.Count} surfaces across " + $"{num} player(s) from a pool of {_mediaPlan.Sources.Count} source(s), " + $"seed {_mediaPlan.Seed} ({num2} moved off the primary).")); } private IEnumerator DiagVideoStateLoop() { if (!((Object)(object)_videoPlayer == (Object)null)) { while (_takeoverActive && (Object)(object)_videoPlayer != (Object)null) { TakeoverBootstrap.Log.LogInfo((object)($"[Diag/Video] isPrepared={_videoPlayer.isPrepared} isPlaying={_videoPlayer.isPlaying} " + $"isPaused={_videoPlayer.isPaused} frame={_videoPlayer.frame}/{_videoPlayer.frameCount} " + string.Format("time={0:F2} clip='{1}' ", _videoPlayer.time, ((Object)(object)_videoPlayer.clip != (Object)null) ? ((Object)_videoPlayer.clip).name : "null") + "url='" + _videoPlayer.url + "' targetTex-id=" + (((Object)(object)_videoPlayer.targetTexture != (Object)null) ? ((Object)_videoPlayer.targetTexture).GetInstanceID().ToString() : "NULL") + " rt-id=" + (((Object)(object)_renderTexture != (Object)null) ? ((Object)_renderTexture).GetInstanceID().ToString() : "NULL"))); yield return (object)new WaitForSeconds(1f); } } } private IEnumerator DiagRTReadbackAfterDelay(float delay) { yield return (object)new WaitForSeconds(delay); if ((Object)(object)_renderTexture == (Object)null) { TakeoverBootstrap.Log.LogInfo((object)"[Diag/RT] readback: _renderTexture is null"); yield break; } yield return (object)new WaitForEndOfFrame(); try { RenderTexture active = RenderTexture.active; RenderTexture.active = _renderTexture; Texture2D val = new Texture2D(4, 4, (TextureFormat)3, false); val.ReadPixels(new Rect(0f, 0f, 4f, 4f), 0, 0); val.Apply(); RenderTexture.active = active; Color val2 = Color.black; Color[] pixels = val.GetPixels(); Color[] array = pixels; foreach (Color val3 in array) { val2 += val3; } val2 /= (float)pixels.Length; Object.Destroy((Object)(object)val); string text = ((val2.r > 0.9f && val2.g > 0.9f && val2.b > 0.9f) ? "WHITE (RT empty — video not decoding)" : ((val2.r < 0.05f && val2.g < 0.05f && val2.b < 0.05f) ? "BLACK (RT cleared/uninitialized)" : "HAS COLOR (video decoding into RT)")); TakeoverBootstrap.Log.LogInfo((object)$"[Diag/RT] readback at t={delay}s: avg=({val2.r:F3},{val2.g:F3},{val2.b:F3}) — {text}"); } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[Diag/RT] readback failed: " + ex.Message)); } } private IEnumerator VerifyOverridesAfterDelay() { yield return (object)new WaitForSeconds(2f); GeneralImprovementsMonitorLease generalImprovementsMonitorLease = _generalImprovementsMonitorLease; if (generalImprovementsMonitorLease != null && generalImprovementsMonitorLease.IsActive) { _generalImprovementsMonitorLease.Maintain(true); TakeoverBootstrap.Log.LogInfo((object)$"[Diag/Post] GeneralImprovements property-block lease owns {_generalImprovementsMonitorLease.BoundCount} surface(s)."); } foreach (var overriddenRenderer in _overriddenRenderers) { MeshRenderer item = overriddenRenderer.renderer; if (!((Object)(object)item == (Object)null)) { Material material; try { material = ((Renderer)item).material; } catch { continue; } if (!((Object)(object)material == (Object)null)) { Texture val = (material.HasProperty("_BaseColorMap") ? material.GetTexture("_BaseColorMap") : null); Texture val2 = (material.HasProperty("_EmissiveColorMap") ? material.GetTexture("_EmissiveColorMap") : null); Texture mainTexture = material.mainTexture; bool flag = (Object)(object)val == (Object)(object)_renderTexture; bool flag2 = (Object)(object)val2 == (Object)(object)_renderTexture; TakeoverBootstrap.Log.LogInfo((object)("[Diag/Post] '" + ((Object)((Component)item).gameObject).name + "' shader='" + ((Object)material.shader).name + "' " + string.Format("_BaseColorMap={0} (ours={1}) ", ((Object)(object)val != (Object)null) ? ((Object)val).name : "null", flag) + string.Format("_EmissiveColorMap={0} (ours={1}) ", ((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "null", flag2) + "mainTex=" + (((Object)(object)mainTexture != (Object)null) ? ((Object)mainTexture).name : "null"))); } } } } private void DisableMonitorGraphic(Behaviour graphic) { if ((Object)(object)graphic == (Object)null) { return; } for (int i = 0; i < _disabledMonitorGraphics.Count; i++) { if ((Object)(object)_disabledMonitorGraphics[i].graphic == (Object)(object)graphic) { return; } } _disabledMonitorGraphics.Add((graphic, graphic.enabled)); graphic.enabled = false; } private static T ResolveVisibleMonitorGraphic(StartOfRound sor, T fallback, string namePrefix) where T : Behaviour { try { Transform val = (((Object)(object)sor != (Object)null && (Object)(object)sor.profitQuotaMonitorBGImage != (Object)null) ? ((Component)sor.profitQuotaMonitorBGImage).transform.parent : null); if ((Object)(object)val == (Object)null) { return fallback; } T val2 = default(T); int num = int.MinValue; T[] componentsInChildren = ((Component)val).GetComponentsInChildren(true); foreach (T val3 in componentsInChildren) { if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3 == (Object)(object)fallback) && ((Object)(object)val3).name.StartsWith(namePrefix, StringComparison.OrdinalIgnoreCase)) { int num2 = 0; if (((Component)(object)val3).gameObject.activeInHierarchy) { num2 += 4; } if (((Component)(object)val3).gameObject.activeSelf) { num2 += 2; } if (((Behaviour)val3).enabled) { num2++; } if (num2 > num) { val2 = val3; num = num2; } } } return ((Object)(object)val2 != (Object)null) ? val2 : fallback; } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[TakeoverManager] Monitor clone resolve for '" + namePrefix + "' failed: " + ex.Message)); return fallback; } } private void AddOverlayMatchingBG(Image bgImage) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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) if (!((Object)(object)bgImage == (Object)null) && !((Object)(object)_renderTexture == (Object)null)) { RectTransform rectTransform = ((Graphic)bgImage).rectTransform; GameObject val = new GameObject("Y4NGZ_MonitorOverlay"); val.transform.SetParent(((Transform)rectTransform).parent, false); RectTransform obj = val.AddComponent(); obj.anchorMin = rectTransform.anchorMin; obj.anchorMax = rectTransform.anchorMax; obj.pivot = rectTransform.pivot; obj.anchoredPosition = rectTransform.anchoredPosition; obj.sizeDelta = rectTransform.sizeDelta; ((Transform)obj).localScale = Vector3.one; RawImage val2 = val.AddComponent(); val2.texture = (Texture)(object)_renderTexture; ((Graphic)val2).color = Color.white; _topMonitorOverlayImages.Add(val2); if (_mediaPlan.Active) { _mediaPaintSites.Add(new TakeoverMediaPaintSite(new TakeoverMediaSurfaceKey("overlay:" + GetHierarchyPath(((Component)bgImage).transform), -1), null, val2)); } } } private void ApplyMonitorOverride(MeshRenderer mr, bool dedicatedScreenMesh = false) { //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mr == (Object)null || (Object)(object)_renderTexture == (Object)null) { return; } Material[] materials; try { materials = ((Renderer)mr).materials; } catch { return; } if (materials == null || materials.Length == 0) { return; } try { Material[] sharedMaterials = ((Renderer)mr).sharedMaterials; TakeoverBootstrap.Log.LogInfo((object)$"[Diag/Slots] '{((Object)mr).name}' slotCount={materials.Length}"); for (int i = 0; i < materials.Length; i++) { string text = ((sharedMaterials != null && i < sharedMaterials.Length && (Object)(object)sharedMaterials[i] != (Object)null) ? ((Object)sharedMaterials[i]).name : ""); Material val = materials[i]; string text2 = (((Object)(object)val != (Object)null) ? ((Object)val).name : ""); string text3 = (((Object)(object)val != (Object)null && (Object)(object)val.shader != (Object)null) ? ((Object)val.shader).name : ""); Texture val2 = null; try { if (sharedMaterials != null && i < sharedMaterials.Length && (Object)(object)sharedMaterials[i] != (Object)null) { val2 = sharedMaterials[i].mainTexture; } } catch { } Texture val3 = null; try { if (sharedMaterials != null && i < sharedMaterials.Length && (Object)(object)sharedMaterials[i] != (Object)null && sharedMaterials[i].HasProperty("_BaseColorMap")) { val3 = sharedMaterials[i].GetTexture("_BaseColorMap"); } } catch { } string text4 = (((Object)(object)val2 != (Object)null) ? (((object)val2).GetType().Name + ":" + ((Object)val2).name) : ""); string text5 = (((Object)(object)val3 != (Object)null) ? (((object)val3).GetType().Name + ":" + ((Object)val3).name) : ""); TakeoverBootstrap.Log.LogInfo((object)$"[Diag/Slots] slot[{i}] sharedMat='{text}' instMat='{text2}' shader='{text3}' mainTex={text4} _BaseColorMap={text5}"); } } catch (Exception ex) { TakeoverBootstrap.Log.LogWarning((object)("[Diag/Slots] dump failed for '" + ((Object)mr).name + "': " + ex.Message)); } if (!_savedHDRPState.ContainsKey(mr)) { (bool, Texture, Texture, Texture, Texture, Color, bool, bool)[] array = new(bool, Texture, Texture, Texture, Texture, Color, bool, bool)[materials.Length]; for (int j = 0; j < materials.Length; j++) { Material val4 = materials[j]; if (!((Object)(object)val4 == (Object)null)) { Texture item = null; try { item = val4.mainTexture; } catch { } Texture item2 = (val4.HasProperty("_BaseColorMap") ? val4.GetTexture("_BaseColorMap") : null); Texture item3 = (val4.HasProperty("_UnlitColorMap") ? val4.GetTexture("_UnlitColorMap") : null); Texture item4 = (val4.HasProperty("_EmissiveColorMap") ? val4.GetTexture("_EmissiveColorMap") : null); Color item5 = (val4.HasProperty("_EmissiveColor") ? val4.GetColor("_EmissiveColor") : Color.black); bool item6 = val4.IsKeywordEnabled("_EMISSION"); bool item7 = val4.IsKeywordEnabled("_EMISSIVE_COLOR_MAP"); array[j] = (true, item, item2, item3, item4, item5, item6, item7); } } _savedHDRPState[mr] = array; } for (int k = 0; k < materials.Length; k++) { Material val5 = materials[k]; if ((Object)(object)val5 == (Object)null) { continue; } string text6 = (((Object)(object)val5.shader != (Object)null) ? ((Object)val5.shader).name : string.Empty); bool num = !dedicatedScreenMesh && text6.IndexOf("HDRP/Lit", StringComparison.Ordinal) >= 0; bool flag = IsShipSystemsPowerMaterial(val5); if (num && !flag) { if (_savedHDRPState.TryGetValue(mr, out (bool, Texture, Texture, Texture, Texture, Color, bool, bool)[] value) && value != null && k < value.Length) { (bool, Texture, Texture, Texture, Texture, Color, bool, bool) tuple = value[k]; tuple.Item1 = false; value[k] = tuple; } } else { PaintScreenMaterial(val5, (Texture)(object)_renderTexture); if (_mediaPlan.Active) { _mediaPaintSites.Add(new TakeoverMediaPaintSite(new TakeoverMediaSurfaceKey(GetHierarchyPath(((Component)mr).transform), k), val5, null)); } } } } private static void PaintScreenMaterial(Material m, Texture texture) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)m == (Object)null) && !((Object)(object)texture == (Object)null)) { try { m.mainTexture = texture; } catch { } if (m.HasProperty("_BaseColorMap")) { m.SetTexture("_BaseColorMap", texture); } if (m.HasProperty("_UnlitColorMap")) { m.SetTexture("_UnlitColorMap", texture); } if (m.HasProperty("_EmissiveColorMap")) { m.SetTexture("_EmissiveColorMap", texture); } if (m.HasProperty("_EmissiveColor")) { m.SetColor("_EmissiveColor", Color.white * 1.5f); } m.EnableKeyword("_EMISSION"); m.EnableKeyword("_EMISSIVE_COLOR_MAP"); } } private bool AlreadyOverridden(MeshRenderer r) { foreach (var overriddenRenderer in _overriddenRenderers) { if ((Object)(object)overriddenRenderer.renderer == (Object)(object)r) { return true; } } return false; } private bool AlreadyDisabled(Behaviour b) { foreach (var disabledComponent in _disabledComponents) { if ((Object)(object)disabledComponent.component == (Object)(object)b) { return true; } } return false; } private static bool IsShipSystemsPowerMaterial(Material material) { if ((Object)(object)material == (Object)null) { return false; } if ((((Object)material).name ?? string.Empty).IndexOf("LGUShipPower", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } Texture texture = null; try { texture = material.mainTexture; } catch { } if (IsShipSystemsPowerTexture(texture)) { return true; } if (material.HasProperty("_BaseColorMap") && IsShipSystemsPowerTexture(material.GetTexture("_BaseColorMap"))) { return true; } if (material.HasProperty("_UnlitColorMap") && IsShipSystemsPowerTexture(material.GetTexture("_UnlitColorMap"))) { return true; } if (material.HasProperty("_EmissiveColorMap") && IsShipSystemsPowerTexture(material.GetTexture("_EmissiveColorMap"))) { return true; } return false; } private static bool IsShipSystemsPowerTexture(Texture texture) { if ((Object)(object)texture != (Object)null) { return string.Equals(((Object)texture).name, "LGUShipSystems_PowerMonitorRT", StringComparison.Ordinal); } return false; } internal static bool IsShipSystemsClaimOwnedMaterial(Material material) { return MonitorClaimProbes.IsClaimed((object)material); } private static bool IsMonitorScreen(MeshRenderer r) { if ((Object)(object)r == (Object)null) { return false; } Material sharedMaterial = ((Renderer)r).sharedMaterial; if ((Object)(object)sharedMaterial == (Object)null) { return false; } if (IsShipSystemsClaimOwnedMaterial(sharedMaterial)) { return false; } string name = ((Object)((Component)r).gameObject).name; if (name.Contains("BodyCam") || name.Contains("Button") || name.Contains("Overlay")) { return false; } if (IsShipSystemsPowerMaterial(sharedMaterial)) { return true; } return ((Object)sharedMaterial).name.StartsWith("TerminalTex", StringComparison.Ordinal); } private static bool MatchesAdditionalMonitorFilter(MeshRenderer renderer) { string text = TakeoverBootstrap.CfgAdditionalMonitorNameTokens?.Value; if (string.IsNullOrWhiteSpace(text)) { return true; } string hierarchyPath = GetHierarchyPath(((Component)renderer).transform); string[] array = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length > 0 && hierarchyPath.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } private static void DumpHierarchy(Transform t, int depth) { if (!((Object)(object)t == (Object)null) && depth <= 6) { string text = new string(' ', depth * 2); MeshRenderer component = ((Component)t).GetComponent(); string text2 = ""; string text3 = ""; if ((Object)(object)component != (Object)null && (Object)(object)((Renderer)component).sharedMaterial != (Object)null && (Object)(object)((Renderer)component).sharedMaterial.mainTexture != (Object)null) { text2 = ((Object)((Renderer)component).sharedMaterial.mainTexture).name; text3 = " " + ((object)((Renderer)component).sharedMaterial.mainTexture).GetType().Name; } TakeoverBootstrap.Log.LogInfo((object)("[TakeoverManager] " + text + ((Object)t).name + " (MR=" + (((Object)(object)component != (Object)null) ? "Y" : "N") + " tex=" + text2 + text3 + ")")); for (int i = 0; i < t.childCount; i++) { DumpHierarchy(t.GetChild(i), depth + 1); } } } private static string GetHierarchyPath(Transform t) { if ((Object)(object)t == (Object)null) { return ""; } List list = new List(); while ((Object)(object)t != (Object)null) { list.Add(((Object)t).name); t = t.parent; } list.Reverse(); return string.Join("/", list); } private static bool IsUnderGeneralImprovementsMonitorGroup(Transform transform) { Transform val = transform; while ((Object)(object)val != (Object)null) { if (((Object)val).name != null && ((Object)val).name.StartsWith("MonitorGroup", StringComparison.Ordinal)) { return true; } val = val.parent; } return false; } private IEnumerator BlitMapScreenLoop() { WaitForEndOfFrame eof = new WaitForEndOfFrame(); bool logged = false; while (_takeoverActive && (Object)(object)_mapScreenRT != (Object)null && (Object)(object)_renderTexture != (Object)null) { yield return eof; if (!logged) { TakeoverBootstrap.Log.LogInfo((object)($"[Diag/Blit] first blit: src=_renderTexture(id={((Object)_renderTexture).GetInstanceID()} {((Texture)_renderTexture).width}x{((Texture)_renderTexture).height}) " + $"dst=_mapScreenRT(id={((Object)_mapScreenRT).GetInstanceID()} {((Texture)_mapScreenRT).width}x{((Texture)_mapScreenRT).height})")); logged = true; } try { Graphics.Blit((Texture)(object)_renderTexture, _mapScreenRT); } catch { } } } private IEnumerator PlayMumbleSequence() { AudioClip[] mumbleClips = TakeoverBootstrap.MumbleClips; string text = "bundle"; if (_useQuotaProgressionPayload) { AudioClip[] mumbleClips2 = TakeoverAudioOverrides.GetMumbleClips(); if (mumbleClips2.Length != 0) { mumbleClips = mumbleClips2; text = "config"; } } TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] PlayMumbleSequence: entered. MumbleClips.Length={mumbleClips.Length} (source={text})"); if (mumbleClips.Length == 0) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] PlayMumbleSequence: no mumble clips loaded — yielding break."); yield break; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] PlayMumbleSequence: StartOfRound.Instance is null — position will default."); } GameObject val = new GameObject("Y4NGZ_MumbleAudio"); Object.DontDestroyOnLoad((Object)(object)val); _mumbleSource = val.AddComponent(); if ((Object)(object)_mumbleSource == (Object)null) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] PlayMumbleSequence: AddComponent returned null — abort."); yield break; } if ((Object)(object)instance?.speakerAudioSource != (Object)null) { val.transform.position = ((Component)instance.speakerAudioSource).transform.position; } else if ((Object)(object)instance != (Object)null) { val.transform.position = ((Component)instance).transform.position; } _mumbleSource.spatialBlend = 0f; _mumbleSource.volume = Mathf.Min(TakeoverBootstrap.CfgMumbleVolume.Value * 1.6f, 1f); _mumbleSource.loop = false; _mumbleSource.playOnAwake = false; AudioHighPassFilter obj = val.AddComponent(); obj.cutoffFrequency = 300f; obj.highpassResonanceQ = 1f; AudioLowPassFilter obj2 = val.AddComponent(); obj2.cutoffFrequency = 2200f; obj2.lowpassResonanceQ = 1.5f; val.AddComponent().distortionLevel = 0.3f; TakeoverBootstrap.Log.LogInfo((object)($"[TakeoverManager] PlayMumbleSequence: source ready. spatial={_mumbleSource.spatialBlend:F2} " + $"vol={_mumbleSource.volume:F2} clips={mumbleClips.Length} (source={text}). Entering loop.")); while (_takeoverActive && (Object)(object)_mumbleSource != (Object)null && mumbleClips.Length != 0) { AudioClip clip = mumbleClips[Random.Range(0, mumbleClips.Length)]; if ((Object)(object)clip == (Object)null) { TakeoverBootstrap.Log.LogWarning((object)"[TakeoverManager] PlayMumbleSequence: picked null clip — skipping frame."); yield return null; continue; } _mumbleSource.clip = clip; _mumbleSource.Play(); TakeoverBootstrap.Log.LogInfo((object)$"[TakeoverManager] PlayMumbleSequence: playing '{((Object)clip).name}' ({clip.length:F2}s) isPlaying={_mumbleSource.isPlaying}"); float elapsed = 0f; while (elapsed < clip.length && _takeoverActive) { elapsed += Time.deltaTime; yield return null; } if (_takeoverActive && !((Object)(object)_mumbleSource == (Object)null)) { float gap = Random.Range(0.4f, 1.2f); float gapElapsed = 0f; while (gapElapsed < gap && _takeoverActive) { gapElapsed += Time.deltaTime; yield return null; } continue; } break; } } private IEnumerator ShowDialogue(string passage) { _dialogueCanvasGO = new GameObject("Y4NGZ_DialogueCanvas"); Object.DontDestroyOnLoad((Object)(object)_dialogueCanvasGO); Canvas obj = _dialogueCanvasGO.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 500; CanvasScaler obj2 = _dialogueCanvasGO.AddComponent(); obj2.uiScaleMode = (ScaleMode)1; obj2.referenceResolution = new Vector2(1920f, 1080f); obj2.matchWidthOrHeight = 1f; _dialogueCanvasGO.AddComponent(); GameObject val = new GameObject("Y4NGZ_DialogueText"); val.transform.SetParent(_dialogueCanvasGO.transform, false); TextMeshProUGUI tmp = val.AddComponent(); ((TMP_Text)tmp).fontSize = 28f; ((Graphic)tmp).color = new Color(0f, 1f, 0.255f, 1f); ((TMP_Text)tmp).alignment = (TextAlignmentOptions)1026; ((TMP_Text)tmp).enableWordWrapping = true; ((TMP_Text)tmp).richText = false; ((TMP_Text)tmp).overflowMode = (TextOverflowModes)0; ((TMP_Text)tmp).text = ""; AssignVanillaFont(tmp); RectTransform rectTransform = ((TMP_Text)tmp).rectTransform; rectTransform.anchorMin = new Vector2(0.1f, 0.02f); rectTransform.anchorMax = new Vector2(0.9f, 0.3f); rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; float charDelay = TakeoverBootstrap.CfgTypewriterSpeed.Value; AudioClip tickClip = TypewriterAudio.CreateTick(); GameObject val2 = new GameObject("Y4NGZ_TypewriterTick"); val2.transform.SetParent(_dialogueCanvasGO.transform); AudioSource tickSrc = val2.AddComponent(); tickSrc.spatialBlend = 0f; tickSrc.volume = 0.8f; tickSrc.playOnAwake = false; string built = ""; for (int i = 0; i < passage.Length; i++) { char c = passage[i]; if ((Object)(object)tmp == (Object)null) { yield break; } built = (((TMP_Text)tmp).text = built + c); if (!char.IsWhiteSpace(c)) { if ((Object)(object)tickSrc != (Object)null) { tickSrc.PlayOneShot(tickClip); } yield return (object)new WaitForSeconds(charDelay); } } while ((Object)(object)_dialogueCanvasGO != (Object)null) { yield return null; } } private IEnumerator CRTTransition() { if ((Object)(object)_dialogueCanvasGO != (Object)null) { Object.Destroy((Object)(object)_dialogueCanvasGO); _dialogueCanvasGO = null; } if ((Object)(object)_mumbleSource != (Object)null) { _mumbleSource.Stop(); } if ((Object)(object)_alarmSource != (Object)null) { _alarmSource.Stop(); } if ((Object)(object)_droneSource != (Object)null) { _droneSource.Stop(); } StopAdditionalMediaPlayback(); StopSoundtrack("CRT transition"); Texture2D white = CRTStatic.CreateSolidTexture(Color.white); Texture2D black = CRTStatic.CreateSolidTexture(Color.black); Texture2D noise = CRTStatic.CreateNoiseTexture(256, 256); PlayStaticBurst(); SetAllMonitorTexture((Texture)(object)white); yield return (object)new WaitForSeconds(0.06f); SetAllMonitorTexture((Texture)(object)noise); yield return (object)new WaitForSeconds(0.25f); SetAllMonitorTexture((Texture)(object)black); yield return (object)new WaitForSeconds(0.15f); Object.Destroy((Object)(object)white, 5f); Object.Destroy((Object)(object)black, 5f); Object.Destroy((Object)(object)noise, 5f); } private void SetAllMonitorTexture(Texture tex) { ShipSystemsTakeoverBridge.UpdateExternalMonitorTakeoverTexture(tex); GeneralImprovementsMonitorLease generalImprovementsMonitorLease = _generalImprovementsMonitorLease; if (generalImprovementsMonitorLease != null) { generalImprovementsMonitorLease.UpdateTexture(tex); } foreach (RawImage topMonitorOverlayImage in _topMonitorOverlayImages) { if ((Object)(object)topMonitorOverlayImage != (Object)null) { topMonitorOverlayImage.texture = tex; } } if ((Object)(object)_videoReelRawImage != (Object)null) { _videoReelRawImage.texture = tex; } foreach (var overriddenRenderer in _overriddenRenderers) { MeshRenderer item = overriddenRenderer.renderer; try { if ((Object)(object)item == (Object)null) { continue; } Material[] materials = ((Renderer)item).materials; if (materials == null) { continue; } foreach (Material val in materials) { if (!((Object)(object)val == (Object)null)) { try { val.mainTexture = tex; } catch { } if (val.HasProperty("_BaseColorMap")) { val.SetTexture("_BaseColorMap", tex); } if (val.HasProperty("_UnlitColorMap")) { val.SetTexture("_UnlitColorMap", tex); } if (val.HasProperty("_EmissiveColorMap")) { val.SetTexture("_EmissiveColorMap", tex); } } } } catch { } } } private void PlayStaticBurst() { //IL_000b: 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_003c: Expected O, but got Unknown AudioClip val = CRTStatic.CreateBurstClip(); GameObject val2 = new GameObject("Y4NGZ_StaticBurst"); AudioSource obj = val2.AddComponent(); obj.spatialBlend = 0f; obj.volume = 0.5f; obj.PlayOneShot(val); Object.Destroy((Object)val2, 1.5f); } private IEnumerator RestoreCoroutine() { if (TakeoverBootstrap.CfgDimLights.Value && _savedLights.Count > 0) { yield return ((MonoBehaviour)this).StartCoroutine(RestoreLights()); } RestoreAll(immediate: false); } private IEnumerator RestoreLights() { List<(Light l, float curI, Color curC, float targI, Color targC)> snapshot = new List<(Light, float, Color, float, Color)>(); foreach (var (val, item, item2) in _savedLights) { if ((Object)(object)val != (Object)null) { snapshot.Add((val, val.intensity, val.color, item, item2)); } } float duration = 1.5f; float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; float num = Mathf.SmoothStep(0f, 1f, elapsed / duration); foreach (var (val2, num2, val3, num3, val4) in snapshot) { if (!((Object)(object)val2 == (Object)null)) { val2.intensity = Mathf.Lerp(num2, num3, num); val2.color = Color.Lerp(val3, val4, num); } } yield return null; } foreach (var (val5, _, _, intensity, color) in snapshot) { if ((Object)(object)val5 != (Object)null) { val5.intensity = intensity; val5.color = color; } } _savedLights.Clear(); } private void RestoreAll(bool immediate) { //IL_05ac: Unknown result type (might be due to invalid IL or missing references) //IL_05b6: Expected O, but got Unknown //IL_0418: Unknown result type (might be due to invalid IL or missing references) //IL_041d: Unknown result type (might be due to invalid IL or missing references) //IL_0434: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_0533: Unknown result type (might be due to invalid IL or missing references) //IL_053d: Expected O, but got Unknown //IL_054a: Unknown result type (might be due to invalid IL or missing references) //IL_0554: Expected O, but got Unknown StartOfRound instance = StartOfRound.Instance; RestoreHUD(); try { foreach (var (component, enabled) in _disabledMonitorGraphics) { SetEnabled(component, enabled); } } catch { } _disabledMonitorGraphics.Clear(); if ((Object)(object)instance != (Object)null) { try { VideoPlayer screenLevelVideoReel = instance.screenLevelVideoReel; if ((Object)(object)screenLevelVideoReel != (Object)null) { screenLevelVideoReel.Stop(); screenLevelVideoReel.clip = _savedVideoClip; screenLevelVideoReel.isLooping = _savedVideoLooping; if (_savedVideoPlaying && (Object)(object)_savedVideoClip != (Object)null) { screenLevelVideoReel.Play(); } } SetEnabled((Behaviour)(object)instance.screenLevelDescription, _levelDescWasEnabled); } catch { } } if ((Object)(object)_videoReelRawImage != (Object)null) { try { _videoReelRawImage.texture = _savedVideoReelTexture; } catch { } _videoReelRawImage = null; _savedVideoReelTexture = null; } foreach (RawImage topMonitorOverlayImage in _topMonitorOverlayImages) { if (!((Object)(object)topMonitorOverlayImage == (Object)null)) { try { ((Behaviour)topMonitorOverlayImage).enabled = false; topMonitorOverlayImage.texture = null; ((Component)topMonitorOverlayImage).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)topMonitorOverlayImage).gameObject); } catch { } } } _topMonitorOverlayImages.Clear(); if (_generalImprovementsLeaseCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_generalImprovementsLeaseCoroutine); _generalImprovementsLeaseCoroutine = null; } GeneralImprovementsMonitorLease generalImprovementsMonitorLease = _generalImprovementsMonitorLease; if (generalImprovementsMonitorLease != null) { generalImprovementsMonitorLease.Release(); } _generalImprovementsMonitorLease = null; foreach (var (val, enabled2) in _disabledComponents) { try { if ((Object)(object)val != (Object)null) { val.enabled = enabled2; } } catch { } } _disabledComponents.Clear(); foreach (var (val2, mainTexture) in _overriddenRenderers) { try { if ((Object)(object)val2 == (Object)null) { continue; } Material[] materials = ((Renderer)val2).materials; if ((Object)(object)((Renderer)val2).sharedMaterial != (Object)null) { ((Renderer)val2).sharedMaterial.mainTexture = mainTexture; } _savedHDRPState.TryGetValue(val2, out (bool, Texture, Texture, Texture, Texture, Color, bool, bool)[] value); for (int i = 0; i < materials.Length; i++) { Material val3 = materials[i]; if ((Object)(object)val3 == (Object)null || value == null || i >= value.Length) { continue; } (bool, Texture, Texture, Texture, Texture, Color, bool, bool) tuple4 = value[i]; if (tuple4.Item1) { try { val3.mainTexture = tuple4.Item2; } catch { } if (val3.HasProperty("_BaseColorMap")) { val3.SetTexture("_BaseColorMap", tuple4.Item3); } if (val3.HasProperty("_UnlitColorMap")) { val3.SetTexture("_UnlitColorMap", tuple4.Item4); } if (val3.HasProperty("_EmissiveColorMap")) { val3.SetTexture("_EmissiveColorMap", tuple4.Item5); } if (val3.HasProperty("_EmissiveColor")) { val3.SetColor("_EmissiveColor", tuple4.Item6); } if (tuple4.Item7) { val3.EnableKeyword("_EMISSION"); } else { val3.DisableKeyword("_EMISSION"); } if (tuple4.Rest.Item1) { val3.EnableKeyword("_EMISSIVE_COLOR_MAP"); } else { val3.DisableKeyword("_EMISSIVE_COLOR_MAP"); } } } ((Renderer)val2).SetPropertyBlock((MaterialPropertyBlock)null); } catch { } } _overriddenRenderers.Clear(); _savedHDRPState.Clear(); if (immediate) { foreach (var (val4, intensity, color) in _savedLights) { try { if ((Object)(object)val4 != (Object)null) { val4.intensity = intensity; val4.color = color; } } catch { } } _savedLights.Clear(); } if (_speakerVolumeSaved && (Object)(object)instance?.speakerAudioSource != (Object)null) { instance.speakerAudioSource.volume = _savedSpeakerVolume; _speakerVolumeSaved = false; } if (_flashCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_flashCoroutine); _flashCoroutine = null; } if (_mapBlitCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_mapBlitCoroutine); _mapBlitCoroutine = null; } _mapScreenRT = null; if (_videoFallbackCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_videoFallbackCoroutine); _videoFallbackCoroutine = null; } if (_signalLostCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_signalLostCoroutine); _signalLostCoroutine = null; } DestroySignalLostFrames(); _usingConfiguredVideoSource = false; if ((Object)(object)_videoPlayer != (Object)null) { _videoPlayer.errorReceived -= new ErrorEventHandler(OnVideoErrorReceived); _videoPlayer.prepareCompleted -= new EventHandler(OnVideoPrepareCompleted); Object.Destroy((Object)(object)_videoPlayer); _videoPlayer = null; } if (_soundtrackCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_soundtrackCoroutine); _soundtrackCoroutine = null; } if ((Object)(object)_soundtrackPlayer != (Object)null) { try { _soundtrackPlayer.Stop(); } catch { } _soundtrackPlayer.errorReceived -= new ErrorEventHandler(OnSoundtrackErrorReceived); Object.Destroy((Object)(object)_soundtrackPlayer); _soundtrackPlayer = null; } if ((Object)(object)_soundtrackDummyRT != (Object)null) { _soundtrackDummyRT.Release(); Object.Destroy((Object)(object)_soundtrackDummyRT); _soundtrackDummyRT = null; } if ((Object)(object)_soundtrackSource != (Object)null) { Object.Destroy((Object)(object)((Component)_soundtrackSource).gameObject); _soundtrackSource = null; } _soundtrackPlan = default(TakeoverSoundtrackPlan); if ((Object)(object)_alarmSource != (Object)null) { Object.Destroy((Object)(object)((Component)_alarmSource).gameObject); _alarmSource = null; } if ((Object)(object)_droneSource != (Object)null) { Object.Destroy((Object)(object)((Component)_droneSource).gameObject); _droneSource = null; } if ((Object)(object)_mumbleSource != (Object)null) { Object.Destroy((Object)(object)((Component)_mumbleSource).gameObject); _mumbleSource = null; } if ((Object)(object)_dialogueCanvasGO != (Object)null) { Object.Destroy((Object)(object)_dialogueCanvasGO); _dialogueCanvasGO = null; } OpenBodyCamsCompat.Restore(); ShipSystemsTakeoverBridge.EndExternalMonitorTakeover(); DestroyAdditionalMediaPlayers(); if ((Object)(object)_renderTexture != (Object)null) { _renderTexture.Release(); Object.Destroy((Object)(object)_renderTexture); _renderTexture = null; } if ((Object)(object)_customImageTexture != (Object)null) { Object.Destroy((Object)(object)_customImageTexture); _customImageTexture = null; } _savedVideoClip = null; } private static bool SetEnabled(Behaviour component, bool enabled) { if ((Object)(object)component == (Object)null) { return false; } bool enabled2 = component.enabled; component.enabled = enabled; return enabled2; } private static void AssignVanillaFont(TextMeshProUGUI tmp) { try { HUDManager instance = HUDManager.Instance; object obj; if (instance == null) { obj = null; } else { TextMeshProUGUI clockNumber = instance.clockNumber; obj = ((clockNumber != null) ? ((TMP_Text)clockNumber).font : null); } if ((Object)obj != (Object)null) { ((TMP_Text)tmp).font = ((TMP_Text)HUDManager.Instance.clockNumber).font; return; } } catch { } try { TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll(); foreach (TMP_FontAsset val in array) { if (((Object)val).name.Contains("3270") || ((Object)val).name.Contains("edunline") || ((Object)val).name.Contains("EdgeOf") || ((Object)val).name.Contains("Consolas")) { ((TMP_Text)tmp).font = val; break; } } } catch { } } } internal readonly struct TakeoverMediaSurfaceKey : IEquatable { internal string Path { get; } internal int MaterialIndex { get; } internal TakeoverMediaSurfaceKey(string path, int materialIndex) { Path = path ?? string.Empty; MaterialIndex = materialIndex; } public bool Equals(TakeoverMediaSurfaceKey other) { if (MaterialIndex == other.MaterialIndex) { return string.Equals(Path, other.Path, StringComparison.Ordinal); } return false; } public override bool Equals(object obj) { if (obj is TakeoverMediaSurfaceKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (((Path != null) ? StringComparer.Ordinal.GetHashCode(Path) : 0) * 397) ^ MaterialIndex; } public override string ToString() { return $"{Path}#{MaterialIndex}"; } } internal static class TakeoverMediaAssignment { internal static void SortDeterministically(List keys) { if (keys != null && keys.Count >= 2) { keys.Sort(CompareKeys); } } internal static int CompareKeys(TakeoverMediaSurfaceKey a, TakeoverMediaSurfaceKey b) { int num = string.CompareOrdinal(a.Path ?? string.Empty, b.Path ?? string.Empty); if (num == 0) { return a.MaterialIndex.CompareTo(b.MaterialIndex); } return num; } internal static int[] BuildPermutation(int seed, int count) { if (count <= 0) { return Array.Empty(); } int[] array = new int[count]; for (int i = 0; i < count; i++) { array[i] = i; } if (count == 1) { return array; } uint num = (uint)seed; if (num == 0) { num = 2654435769u; } for (int num2 = count - 1; num2 > 0; num2--) { num = NextState(num); int num3 = (int)(num % (uint)(num2 + 1)); int num4 = array[num2]; array[num2] = array[num3]; array[num3] = num4; } return array; } private static uint NextState(uint state) { state ^= state << 13; state ^= state >> 17; state ^= state << 5; return state; } internal static int PlayerForSurface(int surfaceIndex, int playerCount, int[] permutation) { if (playerCount <= 1 || surfaceIndex < 0) { return 0; } int num = surfaceIndex % playerCount; if (permutation == null || num >= permutation.Length) { return num % playerCount; } int num2 = permutation[num]; if (num2 < 0 || num2 >= playerCount) { return num % playerCount; } return num2; } internal static int ClampPlayerCount(int cap, int poolCount, int surfaceCount) { int num = cap; if (poolCount < num) { num = poolCount; } if (surfaceCount < num) { num = surfaceCount; } if (num >= 1) { return num; } return 1; } } internal readonly struct TakeoverMediaPlan { internal readonly int Seed; internal readonly int MaxPlayers; internal readonly IReadOnlyList Sources; internal bool Active { get { if (Sources != null) { return Sources.Count > 1; } return false; } } internal int ExtraSourceCount { get { if (!Active) { return 0; } return Sources.Count - 1; } } private TakeoverMediaPlan(int seed, int maxPlayers, IReadOnlyList sources) { Seed = seed; MaxPlayers = maxPlayers; Sources = sources ?? Array.Empty(); } internal static TakeoverMediaPlan Resolve() { TakeoverMediaPlanWire mediaPlan = QuotaProgressionRegistry.GetMediaPlan(); if (!mediaPlan.HasMultipleSources) { return default(TakeoverMediaPlan); } List list = new List(); List list2 = new List(); for (int i = 0; i < mediaPlan.Pool.Length; i++) { string text = mediaPlan.Pool[i]; string expectedHash = ((mediaPlan.Hashes != null && i < mediaPlan.Hashes.Length) ? mediaPlan.Hashes[i] : string.Empty); if (QuotaProgressionRegistry.TryResolveMediaFile(text, expectedHash, out var path) && !list.Contains(path, StringComparer.OrdinalIgnoreCase)) { list.Add(path); } else { list2.Add(text); } } if (list2.Count > 0) { TakeoverBootstrap.Log.LogWarning((object)($"[TakeoverManager] Per-monitor media: {list2.Count} of {mediaPlan.Pool.Length} pool " + "entries are missing on this player, do not match the host copy, or are not cached " + string.Format("yet ({0}); this player shows the {1} ", string.Join(", ", list2.ToArray()), list.Count) + "source(s) it has.")); } if (list.Count < 2) { return default(TakeoverMediaPlan); } return new TakeoverMediaPlan(mediaPlan.Seed, mediaPlan.MaxPlayers, list); } } internal static class TakeoverMonitorOwnership { private sealed class AnnouncementBinding : IMonitorSurfaceBinding { public bool IsAvailable => (Object)(object)StartOfRound.Instance != (Object)null; public MonitorSurfaceInfo Info => new MonitorSurfaceInfo("monitor.announcement", WallBinding.CurrentBacking(), false, "large announcement screens"); public object CaptureSnapshot() { return null; } public void RestoreSnapshot(object snapshot) { } public void Apply(MonitorLease lease) { } } private sealed class WallBinding : IMonitorSurfaceBinding { public bool IsAvailable => (Object)(object)StartOfRound.Instance != (Object)null; public MonitorSurfaceInfo Info => new MonitorSurfaceInfo("monitor.wall", CurrentBacking(), false, "ship monitor wall"); public object CaptureSnapshot() { return null; } public void RestoreSnapshot(object snapshot) { } public void Apply(MonitorLease lease) { } internal static MonitorSurfaceBacking CurrentBacking() { if (GeneralImprovementsMonitors.BetterMonitorsActive) { return (MonitorSurfaceBacking)2; } return (MonitorSurfaceBacking)1; } } internal const string OwnerId = "Y4NGZCompany:MonitorTakeover"; internal const string AnnouncementOwnerId = "Y4NGZCompany:QuotaAnnouncement"; private static bool _registered; private static MonitorLease _wallLease; private static MonitorLease _announcementLease; internal static void EnsureRegistered() { if (!_registered) { _registered = true; MonitorArbiter.RegisterSurface("monitor.wall", (IMonitorSurfaceBinding)(object)new WallBinding()); MonitorArbiter.RegisterSurface("monitor.announcement", (IMonitorSurfaceBinding)(object)new AnnouncementBinding()); ShipLayoutNotifications.Subscribe((Action)OnShipLayoutChanged, 0); } } private static void OnShipLayoutChanged(ShipLayoutChange change) { if (!((ShipLayoutChange)(ref change)).Affects((ShipLayoutChangeScope)1)) { return; } try { TakeoverManager.Instance?.ForceRestore("ship layout changed: " + ((ShipLayoutChange)(ref change)).Reason); } catch (Exception ex) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)("[TakeoverManager] Layout-change restore failed: " + ex.Message)); } } } internal static void ClaimWall(string reason) { //IL_002d: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) EnsureRegistered(); if (_wallLease != null && _wallLease.IsHeld) { return; } MonitorLeaseOutcome val = default(MonitorLeaseOutcome); _wallLease = MonitorArbiter.Acquire(new MonitorLeaseRequest("Y4NGZCompany:MonitorTakeover", "monitor.wall", (MonitorPriority)3, (object)null, 0f, false, reason, (Action)null), ref val); if ((int)val != 0 && (int)val != 1) { ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)$"[TakeoverManager] Monitor-wall lease refused ({val}) for '{reason}'."); } _wallLease = null; } } internal static void ReleaseWall() { if (_wallLease != null) { MonitorArbiter.Release(_wallLease); _wallLease = null; } } internal static bool ClaimAnnouncementScreens(string reason) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) EnsureRegistered(); if (_announcementLease != null && _announcementLease.IsHeld) { return _announcementLease.IsWinner; } _announcementLease = MonitorArbiter.Acquire(new MonitorLeaseRequest("Y4NGZCompany:QuotaAnnouncement", "monitor.announcement", (MonitorPriority)4, (object)null, 0f, false, reason, (Action)null)); return _announcementLease.IsWinner; } internal static void ReleaseAnnouncementScreens() { if (_announcementLease != null) { MonitorArbiter.Release(_announcementLease); _announcementLease = null; } } internal static void ResetForNewSession(string reason) { ReleaseWall(); ReleaseAnnouncementScreens(); MonitorArbiter.ReleaseAll("Y4NGZCompany:MonitorTakeover"); MonitorArbiter.ReleaseAll("Y4NGZCompany:QuotaAnnouncement"); ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogInfo((object)("[TakeoverManager] Monitor claims reset (" + reason + "). " + MonitorArbiter.DescribeOwnership())); } } } internal enum TakeoverSoundtrackKind { None, LocalClip, YoutubeCached, ReuseVideoTrack } internal readonly struct TakeoverSoundtrackPlan { internal readonly TakeoverSoundtrackMode Mode; internal readonly TakeoverSoundtrackKind Kind; internal readonly string ResolvedPath; internal readonly float Volume; internal bool Silences { get { if (Mode == TakeoverSoundtrackMode.Replace) { return Kind != TakeoverSoundtrackKind.None; } return false; } } internal bool Active => Kind != TakeoverSoundtrackKind.None; internal TakeoverSoundtrackPlan(TakeoverSoundtrackMode mode, TakeoverSoundtrackKind kind, string resolvedPath, float volume) { Mode = mode; Kind = kind; ResolvedPath = resolvedPath ?? string.Empty; Volume = volume; } } internal static class TakeoverSoundtrack { private static string _signature = string.Empty; private static TakeoverAudioOverrideFile _file; internal static void Prepare(MonoBehaviour host) { if ((Object)(object)host == (Object)null) { return; } TakeoverSoundtrackPlanWire soundtrackPlan = QuotaProgressionRegistry.GetSoundtrackPlan(); if (soundtrackPlan.IsOff) { _signature = string.Empty; _file = default(TakeoverAudioOverrideFile); return; } if (YoutubeVideoResolver.IsYoutubeUrl(soundtrackPlan.Source)) { _signature = string.Empty; _file = default(TakeoverAudioOverrideFile); YoutubeVideoResolver.Prefetch(soundtrackPlan.Source); return; } string text = soundtrackPlan.Source + "|" + soundtrackPlan.Hash; if (!string.Equals(text, _signature, StringComparison.Ordinal)) { _signature = text; _file = (QuotaProgressionRegistry.TryResolveAudioFile(soundtrackPlan.Source, soundtrackPlan.Hash, out var path) ? new TakeoverAudioOverrideFile(path, soundtrackPlan.Hash) : default(TakeoverAudioOverrideFile)); } TakeoverAudioOverrides.Request(host, _file); } internal static AudioClip GetClip() { return TakeoverAudioOverrides.Resolve(_file); } internal static TakeoverSoundtrackPlan ResolvePlan(string configuredMediaFile) { TakeoverSoundtrackPlanWire soundtrackPlan = QuotaProgressionRegistry.GetSoundtrackPlan(); if (soundtrackPlan.IsOff) { return default(TakeoverSoundtrackPlan); } if (YoutubeVideoResolver.IsYoutubeUrl(soundtrackPlan.Source)) { if (YoutubeVideoResolver.TryNormalizeYoutubeUrl(soundtrackPlan.Source, out var normalizedUrl) && YoutubeVideoResolver.TryNormalizeYoutubeUrl(configuredMediaFile, out var normalizedUrl2) && string.Equals(normalizedUrl, normalizedUrl2, StringComparison.Ordinal)) { return new TakeoverSoundtrackPlan(soundtrackPlan.Mode, TakeoverSoundtrackKind.ReuseVideoTrack, string.Empty, soundtrackPlan.Volume); } if (YoutubeVideoResolver.TryGetCachedVideo(soundtrackPlan.Source, out var path)) { return new TakeoverSoundtrackPlan(soundtrackPlan.Mode, TakeoverSoundtrackKind.YoutubeCached, path, soundtrackPlan.Volume); } YoutubeVideoResolver.Prefetch(soundtrackPlan.Source); ManualLogSource log = TakeoverBootstrap.Log; if (log != null) { log.LogWarning((object)"[TakeoverSoundtrack] The configured soundtrack link is not cached yet; this takeover uses the built-in audio."); } return default(TakeoverSoundtrackPlan); } if ((Object)(object)GetClip() != (Object)null) { return new TakeoverSoundtrackPlan(soundtrackPlan.Mode, TakeoverSoundtrackKind.LocalClip, _file.Path, soundtrackPlan.Volume); } ManualLogSource log2 = TakeoverBootstrap.Log; if (log2 != null) { log2.LogWarning((object)("[TakeoverSoundtrack] Soundtrack '" + soundtrackPlan.Source + "' is missing, does not match the host copy, or has not decoded yet; this takeover uses the built-in audio.")); } return default(TakeoverSoundtrackPlan); } } internal static class TypewriterAudio { internal static AudioClip CreateTick() { int num = 44100; int num2 = (int)((float)num * 0.02f); float[] array = new float[num2]; for (int i = 0; i < num2; i++) { float num3 = Mathf.Exp((0f - (float)i / (float)num2) * 35f); float num4 = Mathf.Sin((float)i * 2f * MathF.PI * 1200f / (float)num); float num5 = Random.value * 2f - 1f; array[i] = (num4 * 0.7f + num5 * 0.3f) * num3 * 0.95f; } AudioClip obj = AudioClip.Create("Y4NGZ_Tick", num2, 1, num, false); obj.SetData(array, 0); return obj; } } } namespace Y4NGZCompany.Bootstrap { internal static class TakeoverBootstrap { public const string PLUGIN_GUID = "com.y4ngz.company.monitortakeover"; public const string PLUGIN_NAME = "Monitor Takeover"; public const string PLUGIN_VERSION = "1.0.1"; private const string BUNDLE_FILENAME = "monitortakeover.bundle"; private const string LEGACY_BUNDLE_FILENAME = "monitortakeover.lethalbundle"; private const string DEFAULT_VIDEO_FILENAME = "y4ngz_monitor_takeover.mp4"; internal static string TakeoverVideoPath; internal static ManualLogSource Log; private static AssetBundle _bundle; internal static VideoClip TakeoverVideoClip; internal static AudioClip[] MumbleClips = Array.Empty(); internal static AudioClip AlarmClip; internal static VideoClip MaskManVideoClip; internal static AudioClip MaskManAudioClip; internal static ConfigEntry CfgEnabled; internal static ConfigEntry CfgTakeoverDuration; internal static ConfigEntry CfgOrbitDelay; internal static ConfigEntry CfgMumbleVolume; internal static ConfigEntry CfgTypewriterSpeed; internal static ConfigEntry CfgHideHUD; internal static ConfigEntry CfgDimLights; internal static ConfigEntry CfgLightDimIntensity; internal static ConfigEntry CfgOverrideFifthMonitor; internal static ConfigEntry CfgAdditionalMonitorNameTokens; internal static ConfigEntry CfgMaskManTailBuffer; internal static ConfigEntry CfgVerboseLogging; internal static ConfigEntry CfgDumpMonitorHierarchyOnOrbit; private static Harmony _harmony; private static ConfigFile Config; internal static void Initialize(ConfigFile config, ManualLogSource logger, Harmony harmony) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) Log = logger; Config = config; BindConfig(); if (!CfgEnabled.Value) { Log.LogInfo((object)"Monitor Takeover v1.0.1 - disabled via config."); return; } ResolveTakeoverVideoPath(); if (!LoadBundle()) { Log.LogError((object)"Asset bundle failed to load - mod is disabled."); return; } _harmony = (Harmony)(((object)harmony) ?? ((object)new Harmony("com.y4ngz.company.monitortakeover"))); _harmony.PatchAll(typeof(Patches)); Patches.EnsureRolloverSubscription(); TakeoverMonitorOwnership.EnsureRegistered(); QuotaUnlockAnnouncement.EnsureInstance(); MonoBehaviour coroutineHost = MonitorTakeoverPlugin.CoroutineHost; QuotaTakeoverDebugController.EnsureAttached((coroutineHost != null) ? ((Component)coroutineHost).gameObject : null); Log.LogInfo((object)("Monitor Takeover v1.0.1 loaded. Video file: " + ((TakeoverVideoPath != null) ? "OK" : "MISSING") + ", Video clip (fallback): " + (((Object)(object)TakeoverVideoClip != (Object)null) ? "OK" : "MISSING") + ", " + $"Mumble clips: {MumbleClips.Length}/5, " + "MaskManVideo: " + (((Object)(object)MaskManVideoClip != (Object)null) ? "OK" : "MISSING") + ", MaskManAudio: " + (((Object)(object)MaskManAudioClip != (Object)null) ? "OK" : "MISSING"))); } private static void BindConfig() { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Expected O, but got Unknown CfgEnabled = Config.Bind("General", "Enabled", true, "Master toggle. Set to false to completely disable the mod."); CfgTakeoverDuration = Config.Bind("General", "Takeover Duration", 11f, "Total seconds the monitor takeover lasts before reverting to normal."); CfgOrbitDelay = Config.Bind("General", "Orbit Delay", 4f, "Seconds to wait after the ship enters orbit before starting the takeover."); CfgMumbleVolume = Config.Bind("Audio", "Mumble Volume", 0.6f, new ConfigDescription("Volume of Y4NGZ mumble audio (0 = silent, 1 = full).", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); CfgTypewriterSpeed = Config.Bind("Presentation", "Typewriter Speed", 0.026f, "Seconds between characters in the typewriter dialogue effect. Lower = faster. Default 0.026 reveals the longest dialogue ~2s before sequence ends."); CfgHideHUD = Config.Bind("Presentation", "Hide HUD", true, "Hide all player HUD elements during the takeover."); CfgDimLights = Config.Bind("Presentation", "Dim Lights", true, "Dim ship interior lights during the takeover."); CfgLightDimIntensity = Config.Bind("Presentation", "Light Dim Intensity", 0.12f, new ConfigDescription("How dim the ship lights go (fraction of original intensity). Lower = darker.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); CfgOverrideFifthMonitor = Config.Bind("Presentation", "Override Additional Monitors", true, "If true, any additional MeshRenderer under MonitorWall beyond the four known monitor meshes is also overridden during takeover (catches small side screens)."); CfgAdditionalMonitorNameTokens = Config.Bind("Presentation", "Additional Monitor Name Tokens", string.Empty, "Optional comma-separated renderer or hierarchy-name filters for additional monitors. Empty overrides every detected extra monitor screen."); CfgMaskManTailBuffer = Config.Bind("Presentation", "Mask Man Tail Buffer", 1.5f, new ConfigDescription("Seconds of held-on-monitor time after the Mask Man audio clip finishes before restoring.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty())); CfgVerboseLogging = Config.Bind("Diagnostics", "Verbose Logging", false, "Log detailed bundle asset names during startup. Leave false for normal play."); CfgDumpMonitorHierarchyOnOrbit = Config.Bind("Diagnostics", "Dump Monitor Hierarchy On Orbit", false, "Writes the full MonitorWall hierarchy to the BepInEx log whenever the ship reaches orbit. Dev-only; leave false for normal play."); } private static string FindBundlePath(string dir, params string[] fileNames) { foreach (string path in fileNames) { string text = Path.Combine(dir, path); if (File.Exists(text)) { return text; } } return null; } private static void ResolveTakeoverVideoPath() { TakeoverVideoPath = null; try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (string.IsNullOrEmpty(directoryName)) { return; } string[] array = new string[2] { Path.Combine(directoryName, "y4ngz_monitor_takeover.mp4"), Path.Combine(Path.Combine(directoryName, "Video"), "y4ngz_monitor_takeover.mp4") }; foreach (string path in array) { if (File.Exists(path)) { TakeoverVideoPath = Path.GetFullPath(path); Log.LogInfo((object)("[LGUMonitorTakeover] Takeover video file: '" + TakeoverVideoPath + "'. This is played through VideoPlayer.url; the bundled clip is only the fallback.")); return; } } Log.LogWarning((object)("[LGUMonitorTakeover] 'y4ngz_monitor_takeover.mp4' was not found in '" + directoryName + "' or its Video\\ subfolder. Falling back to the bundled VideoClip, which cannot decode on most profiles (the bundle was built without transcoding, so the clip only references the authoring machine's source file). Deploy the mp4 beside Y4NGZMonitorTakeover.dll.")); } catch (Exception ex) { TakeoverVideoPath = null; Log.LogWarning((object)("[LGUMonitorTakeover] Takeover video path probe failed: " + ex.Message)); } } private static AssetBundle FindPreloadedBundle(string probeAssetName) { foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { if ((Object)(object)allLoadedAssetBundle == (Object)null) { continue; } try { if (allLoadedAssetBundle.Contains(probeAssetName) || BundleHasMatchingAsset(allLoadedAssetBundle, probeAssetName)) { return allLoadedAssetBundle; } } catch { } } return null; } private static T LoadFirstAsset(AssetBundle bundle, params string[] assetNames) where T : Object { string[] array = assetNames; foreach (string text in array) { if (string.IsNullOrWhiteSpace(text)) { continue; } try { T val = bundle.LoadAsset(text); if ((Object)(object)val != (Object)null) { return val; } } catch { } } string[] bundleAssetNames = GetBundleAssetNames(bundle); array = assetNames; foreach (string text2 in array) { if (string.IsNullOrWhiteSpace(text2)) { continue; } foreach (string text3 in bundleAssetNames) { if (!AssetNameMatches(text3, text2)) { continue; } try { T val2 = bundle.LoadAsset(text3); if ((Object)(object)val2 != (Object)null) { Log.LogInfo((object)("[LGUMonitorTakeover] Loaded " + typeof(T).Name + " '" + ((Object)val2).name + "' via bundle asset '" + text3 + "' for key '" + text2 + "'.")); return val2; } } catch { } } } return default(T); } private static bool BundleHasMatchingAsset(AssetBundle bundle, string assetName) { string[] bundleAssetNames = GetBundleAssetNames(bundle); for (int i = 0; i < bundleAssetNames.Length; i++) { if (AssetNameMatches(bundleAssetNames[i], assetName)) { return true; } } return false; } private static string[] GetBundleAssetNames(AssetBundle bundle) { try { return ((bundle != null) ? bundle.GetAllAssetNames() : null) ?? Array.Empty(); } catch { return Array.Empty(); } } private static bool AssetNameMatches(string bundleAssetName, string requestedName) { if (string.IsNullOrWhiteSpace(bundleAssetName) || string.IsNullOrWhiteSpace(requestedName)) { return false; } string text = NormalizeAssetName(bundleAssetName); string text2 = NormalizeAssetName(requestedName); if (text.Equals(text2, StringComparison.OrdinalIgnoreCase)) { return true; } string assetLeaf = GetAssetLeaf(text); string assetLeaf2 = GetAssetLeaf(text2); if (assetLeaf.Equals(assetLeaf2, StringComparison.OrdinalIgnoreCase)) { return true; } string? fileNameWithoutExtension = Path.GetFileNameWithoutExtension(assetLeaf); string fileNameWithoutExtension2 = Path.GetFileNameWithoutExtension(assetLeaf2); if (fileNameWithoutExtension.Equals(fileNameWithoutExtension2, StringComparison.OrdinalIgnoreCase)) { return true; } if (!text.EndsWith("/" + assetLeaf2, StringComparison.OrdinalIgnoreCase)) { return text.EndsWith("/" + fileNameWithoutExtension2, StringComparison.OrdinalIgnoreCase); } return true; } private static string NormalizeAssetName(string assetName) { return (assetName ?? string.Empty).Replace('\\', '/').Trim(); } private static string GetAssetLeaf(string assetName) { string text = NormalizeAssetName(assetName); int num = text.LastIndexOf('/'); if (num < 0) { return text; } return text.Substring(num + 1); } private static bool LoadBundle() { try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string text = FindBundlePath(directoryName, "monitortakeover.bundle", "monitortakeover.lethalbundle"); if (text == null) { Log.LogError((object)("Bundle not found in " + directoryName + " (looked for monitortakeover.bundle and monitortakeover.lethalbundle).")); Log.LogError((object)"Deploy monitortakeover.bundle or monitortakeover.lethalbundle alongside Y4NGZMonitorTakeover.dll, in the directory named above. It is NOT read from Y4NGZCompany's plugin folder any more."); return false; } AssetBundle val = _bundle ?? FindPreloadedBundle("y4ngz_monitor_takeover") ?? AssetBundle.LoadFromFile(text); if ((Object)(object)val == (Object)null) { Log.LogError((object)"AssetBundle.LoadFromFile returned null (corrupt bundle?)."); return false; } if (CfgVerboseLogging.Value) { try { string[] allAssetNames = val.GetAllAssetNames(); Log.LogInfo((object)$"[LGUMonitorTakeover] Bundle asset dump: {allAssetNames.Length} entries."); string[] array = allAssetNames; foreach (string text2 in array) { Log.LogInfo((object)("[LGUMonitorTakeover] asset: " + text2)); } } catch (Exception ex) { Log.LogWarning((object)("[LGUMonitorTakeover] Bundle asset dump failed: " + ex.Message)); } } TakeoverVideoClip = LoadFirstAsset(val, new string[1] { "y4ngz_monitor_takeover" }); if ((Object)(object)TakeoverVideoClip == (Object)null) { Log.LogWarning((object)"Could not load 'y4ngz_monitor_takeover' from bundle. Monitors will go dark instead of showing video."); } List list = new List(); for (int j = 1; j <= 5; j++) { AudioClip val2 = LoadFirstAsset(val, new string[1] { $"mumble_0{j}" }); if ((Object)(object)val2 != (Object)null) { list.Add(val2); } else { Log.LogWarning((object)$"Could not load 'mumble_0{j}' from bundle."); } } MumbleClips = list.ToArray(); AlarmClip = LoadFirstAsset(val, new string[1] { "alarm_takeover" }); if ((Object)(object)AlarmClip == (Object)null) { Log.LogInfo((object)"Bundle has no 'alarm_takeover' clip - TakeoverManager will fall back to an in-game alarm. (TODO: ship a dedicated takeover alarm asset.)"); } MaskManVideoClip = LoadFirstAsset(val, new string[1] { "mask_man_video" }); if ((Object)(object)MaskManVideoClip == (Object)null) { Log.LogWarning((object)"Could not load 'mask_man_video' from bundle - Mask Man takeover will be skipped. Verify the exact asset name from the bundle dump above."); } MaskManAudioClip = LoadFirstAsset(val, new string[1] { "mask_man_audio" }); if ((Object)(object)MaskManAudioClip == (Object)null) { Log.LogWarning((object)"Could not load 'mask_man_audio' from bundle - Mask Man takeover will be skipped. Verify the exact asset name from the bundle dump above."); } _bundle = val; PreloadBundledAudio(); Log.LogInfo((object)"Bundle loaded successfully (kept mounted so streamed audio clips stay readable)."); return true; } catch (Exception arg) { Log.LogError((object)$"Exception loading bundle: {arg}"); return false; } } private static void PreloadBundledAudio() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 foreach (AudioClip item in new List(MumbleClips) { AlarmClip, MaskManAudioClip }) { if ((Object)(object)item == (Object)null) { continue; } try { if ((int)item.loadState != 2 && !item.LoadAudioData()) { Log.LogWarning((object)("[LGUMonitorTakeover] Audio clip '" + ((Object)item).name + "' would not preload its sample data from the bundle. It will be silent. This normally means the bundle's .resource companion is missing or truncated - re-deploy monitortakeover.lethalbundle.")); } } catch (Exception ex) { Log.LogWarning((object)("[LGUMonitorTakeover] Preloading audio clip '" + ((Object)item).name + "' failed: " + ex.Message)); } } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }