using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Dawn; using GameNetcodeStuff; using Microsoft.CodeAnalysis; using Unity.Netcode; using Unity.Netcode.Components; using UnityEngine; using UnityEngine.AI; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.Video; using Y4NGZFlyingTV.NetcodePatcher; using Y4NGZMonsters.Shared; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Y4NGZFlyingTV")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+25ca94fed298fe073d4c739917e3b7f046e875bf")] [assembly: AssemblyProduct("Y4NGZFlyingTV")] [assembly: AssemblyTitle("Y4NGZFlyingTV")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] [module: NetcodePatchedAssembly] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 Y4NGZFlyingTV { public sealed class FlyingTVAi : EnemyAI { private struct ScreenMaterialTarget { public Renderer Renderer; public int MaterialIndex; public ScreenMaterialTarget(Renderer renderer, int materialIndex) { Renderer = renderer; MaterialIndex = materialIndex; } } private struct PlayerMovementSample { public float Timestamp; public Vector3 Position; public PlayerMovementSample(float timestamp, Vector3 position) { //IL_0008: 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) Timestamp = timestamp; Position = position; } } private const int StateRoaming = 0; private const int StateApproach = 1; private const int StateChecking = 2; private const int StateVideo = 3; private const int StateAttack = 4; private const int StateLeave = 5; private const int StateDead = 6; private const float StopSampleIntervalSeconds = 0.1f; private const float FacingAngleThreshold = 60f; private const float AttackDamage = 25f; private const float AttackCooldown = 1.4f; private const float AttackRange = 2.2f; private const int FlyingTVHealth = 6; private const float VoiceToVideoGapSeconds = 0.15f; private const float PlaybackStartLeadSeconds = 0.35f; private const float VideoNoncomplianceGraceSeconds = 5f; private const float YoutubeResolutionTimeoutSeconds = 90f; private const float VideoPrepareTimeoutSeconds = 20f; private const int StaticTextureWidth = 256; private const int StaticTextureHeight = 144; private const float StaticFrameIntervalSeconds = 1f / 12f; private const float RoamSearchWidth = 70f; private const float RoamSearchPrecision = 4f; private const float RoamSearchRetargetMinSeconds = 3.5f; private const float RoamSearchRetargetMaxSeconds = 7.5f; private const float HoverBobSpeed = 1.2f; private const float HoverBobAmplitude = 0.16f; private const float CeilingClearance = 0.6f; private const float NavMeshRepairIntervalSeconds = 1f; private const float DamageSlowPerHit = 0.09f; private const float MinDamageSpeedMultiplier = 0.55f; private const float DamageWobblePerHit = 0.2f; private const float MaxDamageWobble = 1.1f; private const float DeathExplosionKnockbackRadius = 10f; private const float DeathExplosionMaxKnockback = 34f; private const float DeathExplosionMinKnockback = 12f; private const float DeathDespawnDelaySeconds = 0.35f; private const float DamageStunFreezeSeconds = 1f; private const string EmbeddedScreenSurfaceName = "FlyingTVEmbeddedScreenSurface"; internal const float BodyColliderRadius = 0.65f; internal const float BodyColliderHeight = 1.35f; private static readonly int MovingHash = Animator.StringToHash("Moving"); private static readonly string[] VideoExtensions = new string[6] { ".mp4", ".webm", ".mov", ".m4v", ".ogv", ".avi" }; public Transform visualRoot; public Renderer screenRenderer; public AudioSource videoAudioSource; public AudioClip greetingsClip; public AudioClip noncomplianceClip; public AudioClip complianceClip; public AudioClip propellerLoopClip; public AISearchRoutine facilitySearch = new AISearchRoutine(); private AudioSource _voiceSource; private AudioSource _propellerSource; private Coroutine _checkRoutine; private Coroutine _videoRoutine; private Vector3 _leaveDestination; private Vector3 _lastKnownTargetPosition; private Vector3 _videoHoldPosition; private Quaternion _videoHoldRotation; private float _leaveDeadline; private float _leaveMovementStartTime; private float _nextAttackTime; private float _serverVideoEndTime; private float _serverNoncomplianceEnableTime = float.PositiveInfinity; private float _lastVoiceStartTime; private float _nextRoamSearchRetargetTime; private float _roamAngle; private int _damageHitCount; private bool _checkActive; private bool _videoActive; private bool _selectedVideoPlaying; private bool _destroying; private bool _leaveMovementStarted; private bool _deathExplosionTriggered; private Coroutine _deathDespawnRoutine; private string _videoName = string.Empty; private float _forcedDurationSeconds; private Vector3 _lastPosition; private Vector3 _visualBaseLocalPosition; private bool _hasVisualBaseLocalPosition; private float _hoverHeight; private float _nextNavMeshRepairTime; private float _attackLostAggroSince = -1f; private float _videoNoncomplianceSince = -1f; private float _ignoreDetectionUntil; private float _damageStunUntil; private float _preDamageStunAnimatorSpeed = 1f; private bool _hasMovingParameter; private bool _damageStunFrozen; private AudioClip _lastVoiceClip; private CapsuleCollider _bodyCollider; private Rigidbody _bodyRigidbody; private readonly List _playerMovementSamples = new List(); private readonly RaycastHit[] _movementHits = (RaycastHit[])(object)new RaycastHit[8]; private Material _screenMaterial; private Material _screenBlankMaterial; private int _screenMaterialIndex = -1; private GameObject _screenOverlayObject; private Renderer _screenOverlayRenderer; private readonly List _screenMaterialTargets = new List(); private bool _screenTargetsLogged; private bool _missingScreenTargetLogged; private VideoPlayer _videoPlayer; private Coroutine _playbackRoutine; private RenderTexture _renderTexture; private Texture2D _fallbackTexture; private Texture2D _staticTexture; private Color32[] _staticPixels; private bool _staticPlaying; private float _nextStaticFrameTime; private uint _staticNoiseState; private int _staticFrameIndex; private float FollowDistance => Mathf.Max(1f, FlyingTVConfig.FollowDistance.Value); private float FollowHeight => 2.4f; private float PlayerStopWindowSeconds => Mathf.Max(0.1f, 1f); private float PlayerStopThreshold => 0.35f; private float VideoNoncomplianceDistance => Mathf.Max(FollowDistance, FlyingTVConfig.VideoNoncomplianceDistance.Value); private float TurnAwayAngleThreshold => Mathf.Clamp(FlyingTVConfig.TurnAwayAngleThreshold.Value, 10f, 89f); private float VideoNoncomplianceSustainSeconds => Mathf.Max(0f, FlyingTVConfig.VideoNoncomplianceSustainSeconds.Value); private float ApproachSpeed => Mathf.Max(1f, FlyingTVConfig.ApproachSpeed.Value); private float LeaveSpeed => 7f; private float ChaseLoseAggroDistance => 28f; private float ChaseLoseAggroSeconds => 4f; private float ApproachLoseAggroDistance => Mathf.Max(FlyingTVConfig.DetectionRange.Value + 5f, 45f); private float ApproachLoseAggroSeconds => 8f; private float ConfiguredHoverHeight => 1.9f; private float RoamSpeed => Mathf.Max(0.1f, FlyingTVConfig.RoamSpeed.Value); private float ChaseSpeed => Mathf.Max(0.1f, FlyingTVConfig.ChaseSpeed.Value); private float DamageSpeedMultiplier => Mathf.Max(0.55f, 1f - (float)_damageHitCount * 0.09f); private float DamageWobbleAmount => Mathf.Min(1.1f, (float)_damageHitCount * 0.2f); private bool AgentReady { get { if ((Object)(object)base.agent != (Object)null && ((Behaviour)base.agent).enabled) { return base.agent.isOnNavMesh; } return false; } } public override void OnNetworkSpawn() { //IL_0102: Unknown result type (might be due to invalid IL or missing references) ((NetworkBehaviour)this).OnNetworkSpawn(); EnsureNavigationAgent(); ResolveReferences(); EnsureBehaviourStates(); ConfigureSearchDefaults(); _voiceSource = EnsureAudioSource(base.creatureVoice, "FlyingTVVoice", 34f); base.creatureVoice = _voiceSource; _propellerSource = EnsureAudioSource(_propellerSource, "FlyingTVPropellerLoop", 38f); EnsurePropellerLoopPlaying(); StartStaticPlayback(); _roamAngle = Random.Range(0f, 360f); _hoverHeight = ConfiguredHoverHeight; if (((NetworkBehaviour)this).IsServer) { _videoName = FlyingTVConfig.ResolveVideoSource(); _forcedDurationSeconds = ((FlyingTVConfig.PlaybackDurationSeconds != null) ? Mathf.Max(0f, FlyingTVConfig.PlaybackDurationSeconds.Value) : 0f); PrefetchYoutubePlaybackClientRpc(_videoName); SnapToNavMesh(logAlways: true); base.currentBehaviourStateIndex = 0; ((EnemyAI)this).SwitchToBehaviourState(0); base.movingTowardsTargetPlayer = false; base.moveTowardsDestination = true; StartFacilitySearch(((Component)this).transform.position, 70f); } } public override void Start() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) ((EnemyAI)this).Start(); EnsureNavigationAgent(); Plugin.EnsureScanNode(((Component)this).gameObject); EnsureEyeTransform(); EnsureCollisionBody(); ConfigureSearchDefaults(); base.enemyHP = 6; _lastPosition = ((Component)this).transform.position; } public override void FinishedCurrentSearchRoutine() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) ((EnemyAI)this).FinishedCurrentSearchRoutine(); if (base.currentSearch == facilitySearch && base.currentBehaviourStateIndex == 0) { StartFacilitySearch(((Component)this).transform.position, 70f); } } private void EnsureNavigationAgent() { if ((Object)(object)base.agent == (Object)null) { base.agent = ((Component)this).GetComponentInChildren(true); } if ((Object)(object)base.agent == (Object)null) { base.agent = ((Component)this).gameObject.AddComponent(); } base.agent.updatePosition = true; base.agent.updateRotation = false; base.agent.updateUpAxis = false; ((Behaviour)base.agent).enabled = true; ConfigureAgent(); } private void ConfigureAgent() { if (!((Object)(object)base.agent == (Object)null) && ((Behaviour)base.agent).enabled) { base.agent.speed = RoamSpeed; base.agent.acceleration = 10f; base.agent.angularSpeed = 360f; base.agent.stoppingDistance = 0.6f; base.agent.radius = 0.65f; base.agent.height = 1.35f; base.agent.autoBraking = true; base.agent.autoRepath = true; base.agent.autoTraverseOffMeshLink = true; base.agent.obstacleAvoidanceType = (ObstacleAvoidanceType)4; base.agent.avoidancePriority = 45; if (base.agent.areaMask == 0) { base.agent.areaMask = -1; } if (base.openDoorSpeedMultiplier <= 0f) { base.openDoorSpeedMultiplier = 1f; } ApplyHoverHeight(); } } private void ApplyHoverHeight() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0043: 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_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_006a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)base.agent == (Object)null) && ((Behaviour)base.agent).enabled) { float num = ConfiguredHoverHeight; Vector3 val = ((Component)this).transform.position - Vector3.up * base.agent.baseOffset; float num2 = num + 0.675f + 0.6f; RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(val + Vector3.up * 0.1f, Vector3.up, ref val2, num2, GetWorldCollisionMask(), (QueryTriggerInteraction)1)) { num = Mathf.Clamp(((RaycastHit)(ref val2)).distance + 0.1f - 0.675f - 0.6f, 0f, num); } _hoverHeight = Mathf.MoveTowards(_hoverHeight, num, Mathf.Max(0.5f, num) * 2f * Time.deltaTime); base.agent.baseOffset = _hoverHeight; } } private void RepairOffNavMeshIfNeeded() { if (((NetworkBehaviour)this).IsServer && !((Object)(object)base.agent == (Object)null) && !(Time.time < _nextNavMeshRepairTime) && (!((Behaviour)base.agent).enabled || !base.agent.isOnNavMesh)) { _nextNavMeshRepairTime = Time.time + 1f; SnapToNavMesh(logAlways: false); } } private bool SnapToNavMesh(bool logAlways) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_009a: 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_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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) //IL_010f: Unknown result type (might be due to invalid IL or missing references) if (!((NetworkBehaviour)this).IsServer || (Object)(object)base.agent == (Object)null) { return false; } if (((Behaviour)base.agent).enabled && base.agent.isOnNavMesh) { return true; } Vector3 position = ((Component)this).transform.position; int areaMask = ((base.agent.areaMask == 0) ? (-1) : base.agent.areaMask); if (TrySampleNavMesh(position, 14f, areaMask, out var position2) || TrySampleNearestAINode(position, areaMask, out position2) || TrySampleNavMesh(position, 80f, -1, out position2)) { ((Behaviour)base.agent).enabled = false; ((Component)this).transform.position = position2; ((Behaviour)base.agent).enabled = true; ConfigureAgent(); bool flag = base.agent.Warp(position2); if (logAlways) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[FlyingTV] Placed on the NavMesh at {position2} (from {position}, warp={flag})."); } } if (!flag) { return base.agent.isOnNavMesh; } return true; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)$"[FlyingTV] No NavMesh point found near spawn at {position}; the TV cannot path."); } return false; } private static bool TrySampleNavMesh(Vector3 candidate, float radius, int areaMask, out Vector3 position) { //IL_0000: 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_002b: 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_001e: Unknown result type (might be due to invalid IL or missing references) NavMeshHit val = default(NavMeshHit); if (NavMesh.SamplePosition(candidate, ref val, Mathf.Max(0.25f, radius), areaMask)) { position = ((NavMeshHit)(ref val)).position; return true; } position = Vector3.zero; return false; } private bool TrySampleNearestAINode(Vector3 from, int areaMask, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; GameObject[] allAINodes = base.allAINodes; if (allAINodes == null || allAINodes.Length == 0) { return false; } GameObject val = null; float num = float.PositiveInfinity; foreach (GameObject val2 in allAINodes) { if (!((Object)(object)val2 == (Object)null)) { Vector3 val3 = val2.transform.position - from; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; val = val2; } } } if ((Object)(object)val != (Object)null) { return TrySampleNavMesh(val.transform.position, 12f, areaMask, out position); } return false; } private void EnsureEyeTransform() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)base.eye == (Object)null) { Transform val = ((Component)this).transform.Find("FlyingTVEye"); if ((Object)(object)val == (Object)null) { val = new GameObject("FlyingTVEye").transform; val.SetParent(((Component)this).transform, false); } base.eye = val; } base.eye.localPosition = Vector3.zero; base.eye.localRotation = Quaternion.LookRotation(FlyingTVManager.GetScreenLocalFacingDirection(), Vector3.up); } private void EnsureCollisionBody() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) _bodyCollider = ((Component)this).GetComponent() ?? ((Component)this).gameObject.AddComponent(); ((Collider)_bodyCollider).enabled = true; ((Collider)_bodyCollider).isTrigger = false; _bodyCollider.direction = 1; _bodyCollider.center = Vector3.zero; _bodyCollider.radius = 0.65f; _bodyCollider.height = 1.35f; _bodyRigidbody = ((Component)this).GetComponent() ?? ((Component)this).gameObject.AddComponent(); _bodyRigidbody.isKinematic = true; _bodyRigidbody.useGravity = false; _bodyRigidbody.detectCollisions = true; _bodyRigidbody.collisionDetectionMode = (CollisionDetectionMode)3; } public override void Update() { ((EnemyAI)this).Update(); UpdateStaticPlayback(); if (!base.isEnemyDead) { EnsurePropellerLoopPlaying(); } if (!base.isEnemyDead && !ApplyDamageStunFreeze()) { UpdateAnimation(); TickVisualHover(); if (((NetworkBehaviour)this).IsServer) { UpdateServerMovement(); } } } public override void DoAIInterval() { ((EnemyAI)this).DoAIInterval(); if (!((NetworkBehaviour)this).IsServer || base.isEnemyDead || (Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayersDead) { return; } int currentBehaviourStateIndex = base.currentBehaviourStateIndex; if (currentBehaviourStateIndex != 6 && currentBehaviourStateIndex != 5 && currentBehaviourStateIndex != 1 && currentBehaviourStateIndex != 2 && currentBehaviourStateIndex != 3 && currentBehaviourStateIndex != 4 && currentBehaviourStateIndex == 0 && !(Time.time < _ignoreDetectionUntil)) { PlayerControllerB val = FindDetectedPlayer(); if ((Object)(object)val != (Object)null) { base.targetPlayer = val; StartApproach(); } } } public override void HitEnemy(int force = 1, PlayerControllerB playerWhoHit = null, bool playHitSFX = false, int hitID = -1) { ((EnemyAI)this).HitEnemy(force, playerWhoHit, playHitSFX, hitID); if (base.isEnemyDead) { return; } base.enemyHP -= Mathf.Max(1, force); RegisterDamageHit(); BeginDamageStunFreeze(); if (base.enemyHP <= 0) { DieFromDamage(); } else if ((Object)(object)playerWhoHit != (Object)null) { if (((NetworkBehaviour)this).IsServer) { TriggerNonComplianceFromAttack(playerWhoHit); } else if (((NetworkBehaviour)this).IsSpawned) { ReportAttackHitServerRpc(playerWhoHit.playerClientId); } } } public override void KillEnemy(bool destroy = false) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (!base.isEnemyDead) { _destroying = true; StopAllServerRoutines(); if (((NetworkBehaviour)this).IsServer) { StopPlaybackClientRpc(); } else { StopLocalPlayback(resumeStatic: false); } CleanupVideoResources(); StopPropellerLoop(); ClearDamageStunFreeze(); if ((Object)(object)_voiceSource != (Object)null) { _voiceSource.Stop(); } TriggerDeathExplosionPresentation(((Component)this).transform.position); ((EnemyAI)this).SwitchToBehaviourState(6); base.movingTowardsTargetPlayer = false; base.moveTowardsDestination = false; ((EnemyAI)this).KillEnemy(destroy); base.isEnemyDead = true; if (((NetworkBehaviour)this).IsServer && _deathDespawnRoutine == null && (Object)(object)((NetworkBehaviour)this).NetworkObject != (Object)null && ((NetworkBehaviour)this).NetworkObject.IsSpawned) { _deathDespawnRoutine = ((MonoBehaviour)this).StartCoroutine(DeathDespawnRoutine()); } } } public override void OnDestroy() { _destroying = true; CleanupVideoResources(); ((EnemyAI)this).OnDestroy(); } internal void InitializeServer(int targetPlayerIndex, string videoName, float durationSeconds) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (((NetworkBehaviour)this).IsServer) { ResolveReferences(); _videoName = videoName ?? string.Empty; _forcedDurationSeconds = Mathf.Max(0f, durationSeconds); PrefetchYoutubePlaybackClientRpc(_videoName); PlayerControllerB player = FlyingTVManager.GetPlayer(targetPlayerIndex); if (FlyingTVManager.IsValidLivingPlayer(player)) { _lastKnownTargetPosition = ((Component)player).transform.position; StartFacilitySearch(((Component)player).transform.position, 70f); } } } private void StartApproach() { if (base.currentBehaviourStateIndex == 0) { StopSearchIfNeeded(clear: false); base.movingTowardsTargetPlayer = true; base.moveTowardsDestination = true; if ((Object)(object)base.targetPlayer != (Object)null) { ((EnemyAI)this).SetMovingTowardsTargetPlayer(base.targetPlayer); } SetAgentSpeed(ApproachSpeed); ((EnemyAI)this).SwitchToBehaviourState(1); PlayVoiceClip(greetingsClip); } } private void StartCheckPhase() { if (!_checkActive) { _checkActive = true; _playerMovementSamples.Clear(); base.movingTowardsTargetPlayer = false; base.moveTowardsDestination = false; StopAgentForHold(); ((EnemyAI)this).SwitchToBehaviourState(2); _checkRoutine = ((MonoBehaviour)this).StartCoroutine(CheckRoutine()); } } private IEnumerator CheckRoutine() { while (((NetworkBehaviour)this).IsServer && !base.isEnemyDead && _checkActive && base.currentBehaviourStateIndex == 2 && FlyingTVManager.IsValidLivingPlayer(base.targetPlayer)) { PlayerControllerB targetPlayer = base.targetPlayer; float time = Time.time; float playerStopWindowSeconds = PlayerStopWindowSeconds; Vector3 position = ((Component)targetPlayer).transform.position; _playerMovementSamples.Add(new PlayerMovementSample(time, position)); float num = time - playerStopWindowSeconds; while (_playerMovementSamples.Count > 1 && _playerMovementSamples[1].Timestamp <= num) { _playerMovementSamples.RemoveAt(0); } bool flag = _playerMovementSamples[0].Timestamp <= num; if (flag) { for (int i = 0; i < _playerMovementSamples.Count; i++) { if (Vector3.Distance(position, _playerMovementSamples[i].Position) > PlayerStopThreshold) { flag = false; break; } } } if (flag && IsWithinFollowRange(targetPlayer)) { bool num2 = IsPlayerFacingTV(targetPlayer, 60f); _checkActive = false; _checkRoutine = null; _playerMovementSamples.Clear(); if (num2) { StartVideoPhase(); } else { StartAttack(); } yield break; } yield return (object)new WaitForSeconds(0.1f); } _checkActive = false; _checkRoutine = null; _playerMovementSamples.Clear(); } private void StartVideoPhase() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_0043: Unknown result type (might be due to invalid IL or missing references) if (!_videoActive) { _videoActive = true; _videoNoncomplianceSince = -1f; _serverNoncomplianceEnableTime = float.PositiveInfinity; _videoHoldPosition = ((Component)this).transform.position; _videoHoldRotation = ((Component)this).transform.rotation; base.movingTowardsTargetPlayer = false; base.moveTowardsDestination = false; StopAgentForHold(); ((EnemyAI)this).SwitchToBehaviourState(3); float num = ResolveVideoDuration(); float currentVoiceRemainingSeconds = GetCurrentVoiceRemainingSeconds(); _serverVideoEndTime = Time.time + currentVoiceRemainingSeconds + num; if (_videoRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_videoRoutine); } _videoRoutine = ((MonoBehaviour)this).StartCoroutine(VideoStartAndMonitorRoutine(currentVoiceRemainingSeconds, num)); } } private float ResolveVideoDuration() { if (_forcedDurationSeconds > 0f) { return _forcedDurationSeconds; } return 300f; } private IEnumerator VideoStartAndMonitorRoutine(float videoStartDelay, float videoDuration) { if (videoStartDelay > 0f) { float startTime = Time.time + videoStartDelay; while (_videoActive && !base.isEnemyDead && (Object)(object)base.targetPlayer != (Object)null && Time.time < startTime) { yield return null; } } if (!_videoActive || base.isEnemyDead || (Object)(object)base.targetPlayer == (Object)null) { _videoRoutine = null; yield break; } if (YoutubeVideoResolver.IsYoutubeUrl(_videoName)) { Task localPrefetchTask = YoutubeVideoResolver.ResolveAsync(_videoName); float startTime = Time.realtimeSinceStartup + 90f; while (!localPrefetchTask.IsCompleted && _videoActive && !base.isEnemyDead && (Object)(object)base.targetPlayer != (Object)null && Time.realtimeSinceStartup < startTime) { yield return null; } if (!_videoActive || base.isEnemyDead || (Object)(object)base.targetPlayer == (Object)null) { _videoRoutine = null; yield break; } if (!localPrefetchTask.IsCompleted) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[FlyingTV] YouTube prefetch did not finish before the presentation deadline."); } } } double playbackStartServerTime = GetSynchronizedServerTime() + 0.3499999940395355; _serverVideoEndTime = Time.time + 0.35f + videoDuration; _serverNoncomplianceEnableTime = Time.time + 0.35f + 5f; StartPlaybackClientRpc(_videoName, _forcedDurationSeconds, playbackStartServerTime); while (_videoActive && !base.isEnemyDead && (Object)(object)base.targetPlayer != (Object)null) { if (ShouldTriggerVideoNonCompliance(base.targetPlayer)) { StopPlaybackClientRpc(); _videoActive = false; _videoRoutine = null; StartAttack(); yield break; } if (Time.time >= _serverVideoEndTime) { CompleteVideoAndLeave(); _videoRoutine = null; yield break; } yield return (object)new WaitForSeconds(0.25f); } CompleteVideoAndLeave(); _videoRoutine = null; } private void StartAttack() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (base.currentBehaviourStateIndex != 4) { StopAllServerRoutines(); StopPlaybackClientRpc(); StopSearchIfNeeded(clear: false); base.movingTowardsTargetPlayer = true; base.moveTowardsDestination = true; if ((Object)(object)base.targetPlayer != (Object)null) { _lastKnownTargetPosition = ((Component)base.targetPlayer).transform.position; ((EnemyAI)this).SetMovingTowardsTargetPlayer(base.targetPlayer); } SetAgentSpeed(ChaseSpeed); ((EnemyAI)this).SwitchToBehaviourState(4); _attackLostAggroSince = -1f; PlayVoiceClip(noncomplianceClip); } } private void StartLeave(float movementDelaySeconds = 0f) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: 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) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) if (base.currentBehaviourStateIndex != 5) { StopAllServerRoutines(); StopSearchIfNeeded(clear: false); base.movingTowardsTargetPlayer = false; base.moveTowardsDestination = false; ((EnemyAI)this).SwitchToBehaviourState(5); Vector3 val = (((Object)(object)base.targetPlayer != (Object)null) ? FlyingTVManager.FlatDirection(((Component)this).transform.position - ((Component)base.targetPlayer).transform.position, -((Component)base.targetPlayer).transform.forward) : ((Component)this).transform.forward); _leaveDestination = ((Component)this).transform.position + val * 16f; if (TrySampleNavMesh(_leaveDestination, 16f, ((Object)(object)base.agent != (Object)null && base.agent.areaMask != 0) ? base.agent.areaMask : (-1), out var position)) { _leaveDestination = position; } _leaveMovementStartTime = Time.time + Mathf.Max(0f, movementDelaySeconds); _leaveDeadline = _leaveMovementStartTime + 3f; _leaveMovementStarted = false; StopAgentForHold(); if (movementDelaySeconds <= 0f) { BeginLeaveMovement(); } } } private void BeginLeaveMovement() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (!_leaveMovementStarted) { _leaveMovementStarted = true; base.moveTowardsDestination = true; SetAgentSpeed(LeaveSpeed); if (AgentReady) { base.agent.isStopped = false; ((EnemyAI)this).SetDestinationToPosition(_leaveDestination, true); } } } private void DieFromDamage() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (!base.isEnemyDead) { StopAllServerRoutines(); if (((NetworkBehaviour)this).IsServer) { StopPlaybackClientRpc(); DeathExplosionClientRpc(((Component)this).transform.position); } else { StopLocalPlayback(resumeStatic: false); TriggerDeathExplosionPresentation(((Component)this).transform.position); } ((EnemyAI)this).SwitchToBehaviourState(6); base.movingTowardsTargetPlayer = false; base.moveTowardsDestination = false; if (((NetworkBehaviour)this).IsOwner || ((NetworkBehaviour)this).IsServer) { ((EnemyAI)this).KillEnemyOnOwnerClient(false); } } } [ClientRpc] private void DeathExplosionClientRpc(Vector3 position) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager != null && networkManager.IsListening) { if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(393840246u, val2, (RpcDelivery)0); ((FastBufferWriter)(ref val)).WriteValueSafe(ref position); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 393840246u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; TriggerDeathExplosionPresentation(position); } } } private void TriggerDeathExplosionPresentation(Vector3 position) { //IL_0037: 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) if (!_deathExplosionTriggered) { _deathExplosionTriggered = true; StopLocalPlayback(resumeStatic: false); StopPropellerLoop(); if ((Object)(object)_voiceSource != (Object)null) { _voiceSource.Stop(); } SpawnVanillaDeathExplosions(position); ApplyLocalDeathExplosionKnockback(position); HideDeathBody(); } } private void SpawnVanillaDeathExplosions(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) Vector3 val = position + Vector3.up * 0.7f; SpawnVanillaExplosionVisual(val); SpawnVanillaExplosionVisual(val + Vector3.up * 0.9f); SpawnVanillaExplosionVisual(val - Vector3.up * 0.45f); } private static void SpawnVanillaExplosionVisual(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Landmine.SpawnExplosion(position, true, 0f, 0f, 0, 0f, (GameObject)null, false); } private void ApplyLocalDeathExplosionKnockback(Vector3 position) { //IL_002a: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); if (!FlyingTVManager.IsValidLivingPlayer(val)) { return; } Vector3 val2 = ((Component)val).transform.position - position; float magnitude = ((Vector3)(ref val2)).magnitude; if (!(magnitude > 10f)) { val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude < 0.01f) { val2 = ((Component)val).transform.forward; } float num = Mathf.Clamp01(1f - magnitude / 10f); Vector3 val3 = ((Vector3)(ref val2)).normalized * Mathf.Lerp(12f, 34f, num) + Vector3.up * Mathf.Lerp(4f, 9f, num); val.externalForces += val3; if ((Object)(object)val.playerBodyAnimator != (Object)null) { val.playerBodyAnimator.SetTrigger("Damage"); } } } private void HideDeathBody() { Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { val.enabled = false; val.forceRenderingOff = true; } } Collider[] componentsInChildren2 = ((Component)this).GetComponentsInChildren(true); foreach (Collider val2 in componentsInChildren2) { if ((Object)(object)val2 != (Object)null) { val2.enabled = false; } } if ((Object)(object)_bodyRigidbody != (Object)null) { _bodyRigidbody.detectCollisions = false; } } private IEnumerator DeathDespawnRoutine() { yield return (object)new WaitForSeconds(0.35f); if ((Object)(object)((NetworkBehaviour)this).NetworkObject != (Object)null && ((NetworkBehaviour)this).NetworkObject.IsSpawned) { ((NetworkBehaviour)this).NetworkObject.Despawn(true); } _deathDespawnRoutine = null; } private void LoseAttackAggro() { //IL_002a: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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) StopAllServerRoutines(); StopPlaybackClientRpc(); _attackLostAggroSince = -1f; _ignoreDetectionUntil = Time.time + ChaseLoseAggroSeconds; Vector3 start = ((_lastKnownTargetPosition == Vector3.zero) ? ((Component)this).transform.position : _lastKnownTargetPosition); base.targetPlayer = null; base.movingTowardsTargetPlayer = false; base.moveTowardsDestination = true; SetAgentSpeed(RoamSpeed); ((EnemyAI)this).SwitchToBehaviourState(0); StartFacilitySearch(start, 52.5f); } private void StopAllServerRoutines() { if (_checkRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_checkRoutine); _checkRoutine = null; } if (_videoRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_videoRoutine); _videoRoutine = null; } _checkActive = false; _videoActive = false; _playerMovementSamples.Clear(); _videoNoncomplianceSince = -1f; } [ServerRpc(RequireOwnership = false)] private void ReportAttackHitServerRpc(ulong playerClientId) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsClient || networkManager.IsHost)) { ServerRpcParams val2 = default(ServerRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendServerRpc(2863110160u, val2, (RpcDelivery)0); BytePacker.WriteValueBitPacked(val, playerClientId); ((NetworkBehaviour)this).__endSendServerRpc(ref val, 2863110160u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsServer || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; PlayerControllerB val3 = ResolvePlayerByClientId(playerClientId); if ((Object)(object)val3 != (Object)null && !base.isEnemyDead) { TriggerNonComplianceFromAttack(val3); } } } private void TriggerNonComplianceFromAttack(PlayerControllerB attacker) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (((NetworkBehaviour)this).IsServer && !base.isEnemyDead && FlyingTVManager.IsValidLivingPlayer(attacker)) { base.targetPlayer = attacker; _lastKnownTargetPosition = ((Component)attacker).transform.position; if (base.currentBehaviourStateIndex != 4 && base.currentBehaviourStateIndex != 6 && base.currentBehaviourStateIndex != 5) { StartAttack(); } } } private void RegisterDamageHit() { _damageHitCount = Mathf.Clamp(_damageHitCount + 1, 0, 10); _roamAngle += Random.Range(12f, 38f); } private void BeginDamageStunFreeze() { _damageStunUntil = Mathf.Max(_damageStunUntil, Time.time + 1f); ApplyDamageStunFreeze(); } private bool ApplyDamageStunFreeze() { if (!(base.stunNormalizedTimer >= 0f) && !(Time.time < _damageStunUntil)) { ClearDamageStunFreeze(); return false; } PauseAgent(); if (!_damageStunFrozen) { _preDamageStunAnimatorSpeed = (((Object)(object)base.creatureAnimator != (Object)null) ? Mathf.Max(0.01f, base.creatureAnimator.speed) : 1f); _damageStunFrozen = true; } if ((Object)(object)base.creatureAnimator != (Object)null) { base.creatureAnimator.speed = 0f; } return true; } private void ClearDamageStunFreeze() { if (_damageStunFrozen) { if ((Object)(object)base.creatureAnimator != (Object)null) { base.creatureAnimator.speed = _preDamageStunAnimatorSpeed; } _damageStunFrozen = false; } } private void ConfigureSearchDefaults() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown if (facilitySearch == null) { facilitySearch = new AISearchRoutine(); } facilitySearch.loopSearch = true; facilitySearch.randomized = true; facilitySearch.onlySearchNodesInLOS = false; facilitySearch.searchPrecision = 4f; if (facilitySearch.searchWidth <= 0f) { facilitySearch.searchWidth = 70f; } } private void EnsureRoamingSearch() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (((NetworkBehaviour)this).IsServer && !base.isEnemyDead && base.currentBehaviourStateIndex == 0 && (facilitySearch == null || base.currentSearch != facilitySearch || !facilitySearch.inProgress) && !(Time.time < _nextRoamSearchRetargetTime)) { StartFacilitySearch(((Component)this).transform.position, 70f); } } private void StartFacilitySearch(Vector3 start, float width) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (((NetworkBehaviour)this).IsServer && !base.isEnemyDead) { ConfigureSearchDefaults(); facilitySearch.searchWidth = Mathf.Clamp(width, 8f, 160f); facilitySearch.searchPrecision = 4f; if (!CanUseVanillaSearch()) { _nextRoamSearchRetargetTime = Time.time + 0.5f; return; } StopSearchIfNeeded(clear: false); ((EnemyAI)this).StartSearch(start, facilitySearch); _nextRoamSearchRetargetTime = Time.time + Random.Range(3.5f, 7.5f); } } private bool CanUseVanillaSearch() { if (((NetworkBehaviour)this).IsOwner && AgentReady && base.allAINodes != null) { return base.allAINodes.Length != 0; } return false; } private void StopSearchIfNeeded(bool clear) { if (base.currentSearch != null && base.currentSearch.inProgress && ((NetworkBehaviour)this).IsOwner) { ((EnemyAI)this).StopSearch(base.currentSearch, clear); } } private void UpdateRoamingSearch() { SetAgentSpeed(RoamSpeed); EnsureRoamingSearch(); float damageWobbleAmount = DamageWobbleAmount; _roamAngle += Time.deltaTime * (0.6f + damageWobbleAmount * 0.45f); FaceTravelDirection(); } private void FaceTravelDirection() { //IL_001b: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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) //IL_004a: 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_0054: 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_009a: 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) Vector3 val = (((Object)(object)base.agent != (Object)null && ((Behaviour)base.agent).enabled) ? base.agent.velocity : Vector3.zero); val.y = 0f; Quaternion val2 = ((!(((Vector3)(ref val)).sqrMagnitude > 0.04f)) ? FlyingTVManager.BuildScreenFacingRotation(new Vector3(Mathf.Cos(_roamAngle * 0.5f), 0f, Mathf.Sin(_roamAngle * 0.5f))) : FlyingTVManager.BuildScreenFacingRotation(((Vector3)(ref val)).normalized)); ((Component)this).transform.rotation = Quaternion.Slerp(((Component)this).transform.rotation, val2, Mathf.Clamp01(Time.deltaTime * 2.5f)); } private PlayerControllerB FindDetectedPlayer() { float num = Mathf.Max(1f, FlyingTVConfig.DetectionRange.Value); float num2 = Mathf.Clamp(FlyingTVConfig.DetectionConeDegrees.Value, 1f, 360f); int num3 = Mathf.RoundToInt(3f); PlayerControllerB val = ((EnemyAI)this).CheckLineOfSightForPlayer(num2, Mathf.RoundToInt(num), num3); if (!FlyingTVManager.IsValidLivingPlayer(val)) { return null; } return val; } private static PlayerControllerB ResolvePlayerByClientId(ulong clientId) { PlayerControllerB[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.allPlayerScripts : null); if (array == null) { return null; } foreach (PlayerControllerB val in array) { if ((Object)(object)val != (Object)null && val.playerClientId == clientId) { return val; } } return null; } private void UpdateServerMovement() { //IL_004a: 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_00d2: Unknown result type (might be due to invalid IL or missing references) int currentBehaviourStateIndex = base.currentBehaviourStateIndex; if (currentBehaviourStateIndex == 6) { return; } RepairOffNavMeshIfNeeded(); ApplyHoverHeight(); switch (currentBehaviourStateIndex) { case 0: UpdateRoamingSearch(); return; case 5: UpdateLeave(); return; } PlayerControllerB targetPlayer = base.targetPlayer; if (!FlyingTVManager.IsValidLivingPlayer(targetPlayer)) { LoseAttackAggro(); return; } _lastKnownTargetPosition = ((Component)targetPlayer).transform.position; switch (currentBehaviourStateIndex) { case 1: if (ShouldLoseAggro(targetPlayer, ApproachLoseAggroDistance, ApproachLoseAggroSeconds)) { LoseAttackAggro(); break; } SetAgentSpeed(ApproachSpeed); FaceTarget(targetPlayer); if (!_checkActive && IsWithinFollowRange(targetPlayer)) { StartCheckPhase(); } break; case 2: if (ShouldLoseAggro(targetPlayer, ApproachLoseAggroDistance, ApproachLoseAggroSeconds)) { LoseAttackAggro(); } else { FollowTargetDuringCheck(targetPlayer); } break; case 3: ((Component)this).transform.rotation = _videoHoldRotation; break; case 4: ChaseAndAttackTarget(targetPlayer); break; } } private void SetAgentSpeed(float speed) { if (!((Object)(object)base.agent == (Object)null) && ((Behaviour)base.agent).enabled) { base.agent.speed = Mathf.Max(0.1f, speed * DamageSpeedMultiplier); if (base.agent.isOnNavMesh) { base.agent.isStopped = false; } } } private void PauseAgent() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (AgentReady) { base.agent.isStopped = true; base.agent.velocity = Vector3.zero; } } private void StopAgentForHold() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) StopSearchIfNeeded(clear: false); if (AgentReady) { base.agent.isStopped = true; base.agent.ResetPath(); base.agent.velocity = Vector3.zero; } } private void FollowTargetDuringCheck(PlayerControllerB target) { FaceTarget(target); if (IsWithinFollowRange(target)) { if (base.movingTowardsTargetPlayer || base.moveTowardsDestination || (AgentReady && !base.agent.isStopped)) { base.movingTowardsTargetPlayer = false; base.moveTowardsDestination = false; StopAgentForHold(); } return; } if (!base.movingTowardsTargetPlayer || !base.moveTowardsDestination) { base.movingTowardsTargetPlayer = true; base.moveTowardsDestination = true; ((EnemyAI)this).SetMovingTowardsTargetPlayer(target); } SetAgentSpeed(ApproachSpeed); } private bool IsWithinFollowRange(PlayerControllerB target) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)target).transform.position - ((Component)this).transform.position; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude <= FollowDistance * FollowDistance) { return HasLineOfSightToTarget(target); } return false; } private void UpdateLeave() { if (FlyingTVManager.IsValidLivingPlayer(base.targetPlayer)) { FaceTarget(base.targetPlayer); } if (!(Time.time < _leaveMovementStartTime)) { BeginLeaveMovement(); SetAgentSpeed(LeaveSpeed); if (Time.time >= _leaveDeadline && (Object)(object)((NetworkBehaviour)this).NetworkObject != (Object)null && ((NetworkBehaviour)this).NetworkObject.IsSpawned) { ((NetworkBehaviour)this).NetworkObject.Despawn(true); } } } private void ChaseAndAttackTarget(PlayerControllerB target) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (ShouldLoseAggro(target, ChaseLoseAggroDistance, ChaseLoseAggroSeconds)) { LoseAttackAggro(); return; } SetAgentSpeed(ChaseSpeed); FaceTarget(target); if (!(Time.time < _nextAttackTime) && Vector3.Distance(((Component)this).transform.position, ((Component)target).transform.position) <= 3.2f) { _nextAttackTime = Time.time + 1.4f; ApplyAttackDamageClientRpc(target.actualClientId, 25); } } [ClientRpc] private void ApplyAttackDamageClientRpc(ulong targetClientId, int damage) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(3566271186u, val2, (RpcDelivery)0); BytePacker.WriteValueBitPacked(val, targetClientId); BytePacker.WriteValueBitPacked(val, damage); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 3566271186u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; PlayerControllerB val3 = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); if (!((Object)(object)val3 == (Object)null) && !val3.isPlayerDead && (val3.actualClientId == targetClientId || val3.playerClientId == targetClientId)) { val3.DamagePlayer(damage, true, true, (CauseOfDeath)1, 0, false, default(Vector3)); } } } private bool ShouldLoseAggro(PlayerControllerB target, float loseDistance, float loseSeconds) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) float num = Vector3.Distance(((Component)this).transform.position, ((Component)target).transform.position); bool flag = num >= loseDistance; bool flag2 = num > 3.2f && !HasLineOfSightToTarget(target); if (!flag && !flag2) { _attackLostAggroSince = -1f; return false; } if (_attackLostAggroSince < 0f) { _attackLostAggroSince = Time.time; return false; } return Time.time - _attackLostAggroSince >= loseSeconds; } private bool HasLineOfSightToTarget(PlayerControllerB target) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)this).transform.position; Vector3 val = ((Component)target).transform.position + Vector3.up * 1.4f - position; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude <= 0.1f) { return true; } int worldCollisionMask = GetWorldCollisionMask(); int num = Physics.RaycastNonAlloc(position, val / magnitude, _movementHits, magnitude, worldCollisionMask, (QueryTriggerInteraction)1); for (int i = 0; i < num; i++) { Collider collider = ((RaycastHit)(ref _movementHits[i])).collider; if (!IsIgnoredMovementCollider(collider)) { return false; } } return true; } private void TickVisualHover() { //IL_001e: 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_0098: 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) //IL_00a9: 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) if (!((Object)(object)visualRoot == (Object)null)) { if (!_hasVisualBaseLocalPosition) { _visualBaseLocalPosition = visualRoot.localPosition; _hasVisualBaseLocalPosition = true; } if (base.isEnemyDead) { visualRoot.localPosition = _visualBaseLocalPosition; return; } float damageWobbleAmount = DamageWobbleAmount; float num = Time.time * 1.2f; float num2 = Mathf.Sin(num * 1.3f) * (0.16f + damageWobbleAmount * 0.09f); float num3 = Mathf.Sin(num * 1.7f + _roamAngle) * damageWobbleAmount * 0.11f; visualRoot.localPosition = _visualBaseLocalPosition + new Vector3(num3, num2, 0f); } } private bool IsIgnoredMovementCollider(Collider hitCollider) { if ((Object)(object)hitCollider == (Object)null) { return true; } Transform transform = ((Component)hitCollider).transform; if ((Object)(object)transform != (Object)null && transform.IsChildOf(((Component)this).transform)) { return true; } if ((Object)(object)((Component)hitCollider).GetComponentInParent() != (Object)null) { return true; } return (Object)(object)((Component)hitCollider).GetComponentInParent() != (Object)null; } private static int GetWorldCollisionMask() { if ((Object)(object)StartOfRound.Instance != (Object)null) { return StartOfRound.Instance.collidersAndRoomMaskAndDefault; } int mask = 1; AddLayerToMask(ref mask, "Room"); AddLayerToMask(ref mask, "MapProps"); AddLayerToMask(ref mask, "Colliders"); return mask; } private static void AddLayerToMask(ref int mask, string layerName) { int num = LayerMask.NameToLayer(layerName); if (num >= 0) { mask |= 1 << num; } } private void FaceTarget(PlayerControllerB target) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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_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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_009d: 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) //IL_00a9: 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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: 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_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: 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_009a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)target == (Object)null)) { Vector3 val = ((Component)target).transform.position + Vector3.up * 1.4f - ((Component)this).transform.position; if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = ((Component)this).transform.forward; } val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = ((Component)this).transform.forward; } Vector3 val2 = GetCurrentScreenSideDirection(); if (((Vector3)(ref val2)).sqrMagnitude < 0.001f) { val2 = ((Component)this).transform.forward; } Quaternion val3 = Quaternion.FromToRotation(((Vector3)(ref val2)).normalized, ((Vector3)(ref val)).normalized) * ((Component)this).transform.rotation; ((Component)this).transform.rotation = Quaternion.Slerp(((Component)this).transform.rotation, val3, Mathf.Clamp01(Time.deltaTime * 8f)); } } private Vector3 GetCurrentScreenSideDirection() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) Vector3 screenFacingDirection = FlyingTVManager.GetScreenFacingDirection(((Component)this).transform); screenFacingDirection.y = 0f; if (((Vector3)(ref screenFacingDirection)).sqrMagnitude > 0.0025f) { return ((Vector3)(ref screenFacingDirection)).normalized; } return ((Component)this).transform.forward; } private bool IsPlayerFacingTV(PlayerControllerB player, float angleThreshold) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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) if ((Object)(object)player == (Object)null) { return false; } Vector3 forward = ((Component)player).transform.forward; forward.y = 0f; ((Vector3)(ref forward)).Normalize(); Vector3 val = ((Component)this).transform.position - ((Component)player).transform.position; val.y = 0f; ((Vector3)(ref val)).Normalize(); if (((Vector3)(ref forward)).sqrMagnitude < 0.001f || ((Vector3)(ref val)).sqrMagnitude < 0.001f) { return true; } return Vector3.Angle(forward, val) <= angleThreshold; } private bool IsPlayerLookingAwayFromTV(PlayerControllerB player, float angleThreshold) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return false; } Transform val = (((Object)(object)player.gameplayCamera != (Object)null) ? ((Component)player.gameplayCamera).transform : ((Component)player).transform); Vector3 val2; if (!((Object)(object)screenRenderer != (Object)null)) { val2 = ((Component)this).transform.position + Vector3.up * 0.5f; } else { Bounds bounds = screenRenderer.bounds; val2 = ((Bounds)(ref bounds)).center; } Vector3 val3 = val2 - val.position; Vector3 forward = val.forward; if (((Vector3)(ref forward)).sqrMagnitude < 0.001f || ((Vector3)(ref val3)).sqrMagnitude < 0.001f) { return false; } float num = Mathf.Cos(angleThreshold * (MathF.PI / 180f)); forward = val.forward; return Vector3.Dot(((Vector3)(ref forward)).normalized, ((Vector3)(ref val3)).normalized) < num; } private bool ShouldTriggerVideoNonCompliance(PlayerControllerB player) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (!FlyingTVManager.IsValidLivingPlayer(player)) { _videoNoncomplianceSince = -1f; return false; } if (Time.time < _serverNoncomplianceEnableTime) { _videoNoncomplianceSince = -1f; return false; } float num = Vector3.Distance(((Component)player).transform.position, ((Component)this).transform.position); bool num2 = num > VideoNoncomplianceDistance; bool flag = IsPlayerLookingAwayFromTV(player, TurnAwayAngleThreshold); if (!(num2 || flag)) { _videoNoncomplianceSince = -1f; return false; } if (_videoNoncomplianceSince < 0f) { _videoNoncomplianceSince = Time.time; } if (Time.time - _videoNoncomplianceSince < VideoNoncomplianceSustainSeconds) { return false; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)($"[FlyingTV] Video noncompliance detected: distance={num:0.0}m " + $"(limit {VideoNoncomplianceDistance:0.0}m), outsideViewCone={flag}.")); } return true; } private void CompleteVideoAndLeave() { if (((NetworkBehaviour)this).IsServer && !base.isEnemyDead && base.currentBehaviourStateIndex == 3) { _videoActive = false; StopPlaybackClientRpc(); PlayVoiceClip(complianceClip); float currentVoiceRemainingSeconds = GetCurrentVoiceRemainingSeconds(); StartLeave(currentVoiceRemainingSeconds); } } private void UpdateAnimation() { //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_0028: 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_0085: 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) if (!((Object)(object)base.creatureAnimator == (Object)null) && _hasMovingParameter) { Vector3 val = ((Component)this).transform.position - _lastPosition; float num = ((Vector3)(ref val)).magnitude / Mathf.Max(Time.deltaTime, 0.001f); bool flag = base.currentBehaviourStateIndex == 1 || base.currentBehaviourStateIndex == 4 || base.currentBehaviourStateIndex == 5 || num > 0.2f; base.creatureAnimator.SetBool(MovingHash, flag); _lastPosition = ((Component)this).transform.position; } } private void PlayVoiceClip(AudioClip clip) { if (!((Object)(object)_voiceSource == (Object)null) && !((Object)(object)clip == (Object)null)) { _voiceSource.Stop(); _voiceSource.clip = clip; _lastVoiceClip = clip; _lastVoiceStartTime = Time.time; _voiceSource.Play(); } } private float GetCurrentVoiceRemainingSeconds() { AudioClip val = (((Object)(object)_voiceSource != (Object)null && (Object)(object)_voiceSource.clip != (Object)null) ? _voiceSource.clip : _lastVoiceClip); if ((Object)(object)val == (Object)null) { return 0f; } if ((Object)(object)_voiceSource != (Object)null && _voiceSource.isPlaying && (Object)(object)_voiceSource.clip == (Object)(object)val) { float num = Mathf.Max(0.01f, Mathf.Abs(_voiceSource.pitch)); float num2 = (val.length - _voiceSource.time) / num; if (!(num2 > 0f)) { return 0f; } return num2 + 0.15f; } float num3 = Time.time - _lastVoiceStartTime; float num4 = val.length - Mathf.Max(0f, num3); if (!(num4 > 0f)) { return 0f; } return num4 + 0.15f; } private void EnsurePropellerLoopPlaying() { if ((Object)(object)_propellerSource == (Object)null) { return; } if ((Object)(object)propellerLoopClip == (Object)null) { propellerLoopClip = Plugin.PropellerClip; } if (!((Object)(object)propellerLoopClip == (Object)null)) { _propellerSource.loop = true; _propellerSource.volume = ((FlyingTVConfig.PropellerVolume != null) ? Mathf.Clamp01(FlyingTVConfig.PropellerVolume.Value) : 0.65f); if ((Object)(object)_propellerSource.clip != (Object)(object)propellerLoopClip) { _propellerSource.clip = propellerLoopClip; } if (!_propellerSource.isPlaying) { _propellerSource.Play(); } } } private void StopPropellerLoop() { if ((Object)(object)_propellerSource != (Object)null) { _propellerSource.Stop(); } } [ClientRpc] private void PrefetchYoutubePlaybackClientRpc(string youtubeUrl) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(2130784982u, val2, (RpcDelivery)0); bool flag = youtubeUrl != null; ((FastBufferWriter)(ref val)).WriteValueSafe(ref flag, default(ForPrimitives)); if (flag) { ((FastBufferWriter)(ref val)).WriteValueSafe(youtubeUrl, false); } ((NetworkBehaviour)this).__endSendClientRpc(ref val, 2130784982u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; if (YoutubeVideoResolver.IsYoutubeUrl(youtubeUrl)) { YoutubeVideoResolver.ResolveAsync(youtubeUrl); } } } [ClientRpc] private void StartPlaybackClientRpc(string videoName, float durationSeconds, double playbackStartServerTime) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager == null || !networkManager.IsListening) { return; } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(3395646845u, val2, (RpcDelivery)0); bool flag = videoName != null; ((FastBufferWriter)(ref val)).WriteValueSafe(ref flag, default(ForPrimitives)); if (flag) { ((FastBufferWriter)(ref val)).WriteValueSafe(videoName, false); } ((FastBufferWriter)(ref val)).WriteValueSafe(ref durationSeconds, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe(ref playbackStartServerTime, default(ForPrimitives)); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 3395646845u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 || (!networkManager.IsClient && !networkManager.IsHost)) { return; } ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; _videoName = videoName ?? string.Empty; _forcedDurationSeconds = Mathf.Max(0f, durationSeconds); if (!_selectedVideoPlaying) { ResolveReferences(); _selectedVideoPlaying = true; if (YoutubeVideoResolver.IsYoutubeUrl(videoName)) { StartYoutubeVideoPlayback(videoName, _forcedDurationSeconds, playbackStartServerTime); return; } string videoLocation = ResolveVideoLocation(videoName); StartVideoPlayback(videoLocation, loop: false, playAudio: true, selectedVideo: true, _forcedDurationSeconds, playbackStartServerTime); } } private void StartYoutubeVideoPlayback(string youtubeUrl, float durationSeconds, double playbackStartServerTime) { if (_playbackRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_playbackRoutine); _playbackRoutine = null; } StopVideoPlayer(); _playbackRoutine = ((MonoBehaviour)this).StartCoroutine(ResolveAndStartYoutubePlayback(youtubeUrl, durationSeconds, playbackStartServerTime)); } private IEnumerator ResolveAndStartYoutubePlayback(string youtubeUrl, float durationSeconds, double playbackStartServerTime) { Task resolutionTask = YoutubeVideoResolver.ResolveAsync(youtubeUrl); float resolutionDeadline = Time.realtimeSinceStartup + 90f; while (!resolutionTask.IsCompleted && _selectedVideoPlaying && !_destroying && !base.isEnemyDead && Time.realtimeSinceStartup < resolutionDeadline) { yield return null; } if (!_selectedVideoPlaying || _destroying || base.isEnemyDead) { _playbackRoutine = null; yield break; } YoutubeVideoStreams youtubeVideoStreams = null; if (!resolutionTask.IsCompleted) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[FlyingTV] Local YouTube resolution timed out; showing the fallback screen."); } } else if (!resolutionTask.IsCanceled && !resolutionTask.IsFaulted) { youtubeVideoStreams = resolutionTask.Result; } _playbackRoutine = null; StartVideoPlayback(youtubeVideoStreams?.VideoUrl, loop: false, playAudio: true, selectedVideo: true, durationSeconds, playbackStartServerTime); } [ClientRpc] private void StopPlaybackClientRpc() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_00c1: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager != null && networkManager.IsListening) { if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsServer || networkManager.IsHost)) { ClientRpcParams val2 = default(ClientRpcParams); FastBufferWriter val = ((NetworkBehaviour)this).__beginSendClientRpc(2066075155u, val2, (RpcDelivery)0); ((NetworkBehaviour)this).__endSendClientRpc(ref val, 2066075155u, val2, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsClient || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; StopLocalPlayback(resumeStatic: true); } } } [ServerRpc(RequireOwnership = false)] private void SelectedPlaybackFinishedServerRpc(ServerRpcParams rpcParams = default(ServerRpcParams)) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_00c1: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager; if (networkManager != null && networkManager.IsListening) { if ((int)((NetworkBehaviour)this).__rpc_exec_stage != 1 && (networkManager.IsClient || networkManager.IsHost)) { FastBufferWriter val = ((NetworkBehaviour)this).__beginSendServerRpc(2905851093u, rpcParams, (RpcDelivery)0); ((NetworkBehaviour)this).__endSendServerRpc(ref val, 2905851093u, rpcParams, (RpcDelivery)0); } if ((int)((NetworkBehaviour)this).__rpc_exec_stage == 1 && (networkManager.IsServer || networkManager.IsHost)) { ((NetworkBehaviour)this).__rpc_exec_stage = (__RpcExecStage)0; CompleteVideoAndLeave(); } } } private void StartStaticPlayback() { if (_destroying || base.isEnemyDead || _selectedVideoPlaying) { return; } ResolveReferences(); StopVideoPlayer(); EnsureScreenMaterial(); EnsureStaticTexture(); if ((Object)(object)_staticTexture == (Object)null) { ShowFallbackTexture(); return; } _staticPlaying = true; _nextStaticFrameTime = 0f; UpdateStaticPlayback(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[FlyingTV] Started procedural animated static playback."); } } private void StartVideoPlayback(string videoLocation, bool loop, bool playAudio, bool selectedVideo, float durationSeconds, double playbackStartServerTime) { if (!_destroying && !base.isEnemyDead) { if (_playbackRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_playbackRoutine); _playbackRoutine = null; } StopVideoPlayer(); _playbackRoutine = ((MonoBehaviour)this).StartCoroutine(PrepareAndPlayVideo(videoLocation, loop, playAudio, selectedVideo, durationSeconds, playbackStartServerTime)); } } private IEnumerator PrepareAndPlayVideo(string videoLocation, bool loop, bool playAudio, bool selectedVideo, float durationSeconds, double playbackStartServerTime) { EnsureScreenMaterial(); EnsureRenderTexture(); bool flag = IsVideoUrl(videoLocation); if (string.IsNullOrEmpty(videoLocation) || (!flag && !File.Exists(videoLocation))) { if (selectedVideo) { _playbackRoutine = null; HandleSelectedVideoFailed(); } else { ShowFallbackTexture(); _playbackRoutine = null; } yield break; } _videoPlayer = ((Component)this).gameObject.GetComponent(); if ((Object)(object)_videoPlayer == (Object)null) { _videoPlayer = ((Component)this).gameObject.AddComponent(); } _videoPlayer.playOnAwake = false; _videoPlayer.waitForFirstFrame = true; _videoPlayer.skipOnDrop = true; _videoPlayer.aspectRatio = (VideoAspectRatio)5; _videoPlayer.isLooping = loop; _videoPlayer.source = (VideoSource)1; _videoPlayer.url = (flag ? videoLocation : new Uri(videoLocation).AbsoluteUri); _videoPlayer.renderMode = (VideoRenderMode)2; _videoPlayer.targetTexture = _renderTexture; _videoPlayer.audioOutputMode = (VideoAudioOutputMode)(playAudio ? 1 : 0); _videoPlayer.controlledAudioTrackCount = (playAudio ? ((ushort)1) : ((ushort)0)); if (playAudio) { _videoPlayer.SetTargetAudioSource((ushort)0, videoAudioSource); } _videoPlayer.loopPointReached -= new EventHandler(OnVideoLoopPointReached); _videoPlayer.loopPointReached += new EventHandler(OnVideoLoopPointReached); _videoPlayer.errorReceived -= new ErrorEventHandler(OnVideoErrorReceived); _videoPlayer.errorReceived += new ErrorEventHandler(OnVideoErrorReceived); _videoPlayer.Prepare(); float prepareDeadline = Time.realtimeSinceStartup + 20f; while (!_videoPlayer.isPrepared && Time.realtimeSinceStartup < prepareDeadline) { yield return null; } if (!_videoPlayer.isPrepared) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)($"[FlyingTV] Video failed to prepare within {20f:0} seconds: " + GetSafeVideoLocationLabel(videoLocation))); } if (selectedVideo) { _playbackRoutine = null; HandleSelectedVideoFailed(); } else { ShowFallbackTexture(); _playbackRoutine = null; } yield break; } int requestedWidth = (int)((_videoPlayer.width != 0) ? Math.Min(_videoPlayer.width, 2048u) : 1024); int requestedHeight = (int)((_videoPlayer.height != 0) ? Math.Min(_videoPlayer.height, 2048u) : 576); EnsureRenderTexture(requestedWidth, requestedHeight); _videoPlayer.targetTexture = _renderTexture; double num = 0.0; if (selectedVideo && playbackStartServerTime > 0.0) { while (_selectedVideoPlaying && GetSynchronizedServerTime() < playbackStartServerTime) { yield return null; } if (!_selectedVideoPlaying) { yield break; } num = Math.Max(0.0, GetSynchronizedServerTime() - playbackStartServerTime); if (durationSeconds > 0f && num >= (double)durationSeconds) { _playbackRoutine = null; HandleSelectedVideoFinished(); yield break; } SeekPreparedPlayer(_videoPlayer, num); } StopStaticPlayback(); ApplyTextureToScreen((Texture)(object)_renderTexture); _videoPlayer.Play(); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[FlyingTV] Video render target resized to {((Texture)_renderTexture).width}x{((Texture)_renderTexture).height}."); } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)(selectedVideo ? "[FlyingTV] Started selected video playback with picture and audio." : "[FlyingTV] Started local video playback.")); } if (selectedVideo && durationSeconds > 0f) { float num2 = Mathf.Max(0f, durationSeconds - (float)num); if (num2 > 0f) { yield return (object)new WaitForSeconds(num2); } _playbackRoutine = null; HandleSelectedVideoFinished(); } else { _playbackRoutine = null; } } private static void SeekPreparedPlayer(VideoPlayer player, double playbackOffsetSeconds) { if (!((Object)(object)player == (Object)null) && player.isPrepared && !(playbackOffsetSeconds <= 0.05) && player.canSetTime) { double num = playbackOffsetSeconds; if (player.length > 0.0) { num = Math.Min(num, Math.Max(0.0, player.length - 0.05)); } player.time = num; } } private static string GetSafeVideoLocationLabel(string videoLocation) { if (string.IsNullOrWhiteSpace(videoLocation)) { return "no media source"; } if (Uri.TryCreate(videoLocation, UriKind.Absolute, out Uri result) && (result.Scheme == Uri.UriSchemeHttp || result.Scheme == Uri.UriSchemeHttps)) { return result.Scheme + "://" + result.IdnHost + "/..."; } string fileName = Path.GetFileName(videoLocation); if (!string.IsNullOrEmpty(fileName)) { return fileName; } return "local media file"; } private static string GetSafeVideoError(string message) { if (string.IsNullOrWhiteSpace(message)) { return "no decoder diagnostic was returned"; } string text = message.Replace('\r', ' ').Replace('\n', ' ').Trim(); if (text.IndexOf("http", StringComparison.OrdinalIgnoreCase) >= 0) { return "the decoder reported an HTTP media failure (signed URL omitted)"; } if (text.Length > 300) { return text.Substring(0, 300) + "..."; } return text; } private static double GetSynchronizedServerTime() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton != (Object)null)) { return Time.realtimeSinceStartup; } NetworkTime serverTime = singleton.ServerTime; return ((NetworkTime)(ref serverTime)).Time; } private void OnVideoLoopPointReached(VideoPlayer source) { if (_selectedVideoPlaying) { HandleSelectedVideoFinished(); } } private void OnVideoErrorReceived(VideoPlayer source, string message) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[FlyingTV] Video error: " + GetSafeVideoError(message))); } if (_selectedVideoPlaying) { HandleSelectedVideoFailed(); return; } StopVideoPlayer(); ShowFallbackTexture(); } private void HandleSelectedVideoFinished() { //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 (_selectedVideoPlaying) { _selectedVideoPlaying = false; StopVideoPlayer(); StartStaticPlayback(); if (((NetworkBehaviour)this).IsServer) { CompleteVideoAndLeave(); } else if (((NetworkBehaviour)this).IsSpawned && !YoutubeVideoResolver.IsYoutubeUrl(_videoName)) { SelectedPlaybackFinishedServerRpc(); } } } private void HandleSelectedVideoFailed() { if (!_selectedVideoPlaying) { return; } _selectedVideoPlaying = false; StopVideoPlayer(); StartStaticPlayback(); if (((NetworkBehaviour)this).IsServer && _videoActive && base.currentBehaviourStateIndex == 3) { float num = Time.time + 10f; if (_serverVideoEndTime <= Time.time || _serverVideoEndTime > num) { _serverVideoEndTime = num; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"[FlyingTV] Selected video failed; showing animated static for {10f:0} seconds before leaving."); } } } private void StopLocalPlayback(bool resumeStatic) { _selectedVideoPlaying = false; StopVideoPlayer(); StopStaticPlayback(); if (resumeStatic) { StartStaticPlayback(); } } private void StopVideoPlayer() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown if (_playbackRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_playbackRoutine); _playbackRoutine = null; } if ((Object)(object)_videoPlayer != (Object)null) { _videoPlayer.loopPointReached -= new EventHandler(OnVideoLoopPointReached); _videoPlayer.errorReceived -= new ErrorEventHandler(OnVideoErrorReceived); _videoPlayer.Stop(); } if ((Object)(object)videoAudioSource != (Object)null) { videoAudioSource.Stop(); } } private void CleanupVideoResources() { StopLocalPlayback(resumeStatic: false); if ((Object)(object)_renderTexture != (Object)null) { _renderTexture.Release(); Object.Destroy((Object)(object)_renderTexture); _renderTexture = null; } if ((Object)(object)_staticTexture != (Object)null) { Object.Destroy((Object)(object)_staticTexture); _staticTexture = null; _staticPixels = null; } if ((Object)(object)_screenMaterial != (Object)null) { Object.Destroy((Object)(object)_screenMaterial); _screenMaterial = null; _screenMaterialIndex = -1; } if ((Object)(object)_screenBlankMaterial != (Object)null) { Object.Destroy((Object)(object)_screenBlankMaterial); _screenBlankMaterial = null; } if ((Object)(object)_screenOverlayObject != (Object)null) { Object.Destroy((Object)(object)_screenOverlayObject); _screenOverlayObject = null; _screenOverlayRenderer = null; } if ((Object)(object)_fallbackTexture != (Object)null) { Object.Destroy((Object)(object)_fallbackTexture); _fallbackTexture = null; } } private void EnsureScreenMaterial() { CollectScreenMaterialTargets(); if ((Object)(object)_screenMaterial != (Object)null) { ApplyScreenMaterialToTargets(); NeutralizeOriginalScreenSurfaces(); EnsureVideoDisplaySurface(); return; } Material source = null; if (_screenMaterialTargets.Count > 0) { ScreenMaterialTarget screenMaterialTarget = _screenMaterialTargets[0]; Material[] array = (((Object)(object)screenMaterialTarget.Renderer != (Object)null) ? screenMaterialTarget.Renderer.materials : null); if (array != null && screenMaterialTarget.MaterialIndex >= 0 && screenMaterialTarget.MaterialIndex < array.Length) { source = array[screenMaterialTarget.MaterialIndex]; } } else if ((Object)(object)screenRenderer != (Object)null) { Material[] materials = screenRenderer.materials; if (materials != null && materials.Length != 0) { _screenMaterialIndex = ResolveScreenMaterialIndex(materials, _screenMaterialIndex); source = (((Object)(object)materials[_screenMaterialIndex] != (Object)null) ? materials[_screenMaterialIndex] : screenRenderer.sharedMaterial); } } _screenMaterial = CreateRuntimeScreenMaterial(source); ((Object)_screenMaterial).name = "FlyingTV_RuntimeScreen"; ApplyScreenMaterialToTargets(); NeutralizeOriginalScreenSurfaces(); EnsureVideoDisplaySurface(); } private void EnsureRenderTexture(int requestedWidth = 0, int requestedHeight = 0) { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown int num = ((requestedWidth > 0) ? Mathf.Clamp(requestedWidth, 64, 2048) : 1024); int num2 = ((requestedHeight > 0) ? Mathf.Clamp(requestedHeight, 64, 2048) : 576); if ((Object)(object)_renderTexture != (Object)null && ((Texture)_renderTexture).width == num && ((Texture)_renderTexture).height == num2 && _renderTexture.IsCreated()) { return; } if ((Object)(object)_renderTexture != (Object)null) { if ((Object)(object)_videoPlayer != (Object)null && (Object)(object)_videoPlayer.targetTexture == (Object)(object)_renderTexture) { _videoPlayer.targetTexture = null; } _renderTexture.Release(); Object.Destroy((Object)(object)_renderTexture); _renderTexture = null; } _renderTexture = new RenderTexture(num, num2, 0, (RenderTextureFormat)0) { name = "FlyingTVVideoRT", useMipMap = false, autoGenerateMips = false }; _renderTexture.Create(); } private void ShowFallbackTexture() { StopStaticPlayback(); EnsureScreenMaterial(); if ((Object)(object)_fallbackTexture == (Object)null) { _fallbackTexture = BuildFallbackTexture(); } ApplyTextureToScreen((Texture)(object)_fallbackTexture); } private void EnsureStaticTexture() { //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_0034: 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_0047: Expected O, but got Unknown if (!((Object)(object)_staticTexture != (Object)null) || _staticPixels == null) { _staticTexture = new Texture2D(256, 144, (TextureFormat)4, false) { name = "FlyingTVProceduralStatic", wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)0 }; _staticPixels = (Color32[])(object)new Color32[36864]; _staticNoiseState = (uint)(((Object)this).GetInstanceID() ^ -1640531527); if (_staticNoiseState == 0) { _staticNoiseState = 2738958700u; } ApplyTextureToScreen((Texture)(object)_staticTexture); } } private void UpdateStaticPlayback() { //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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) if (!_staticPlaying || (Object)(object)_staticTexture == (Object)null || _staticPixels == null || Time.unscaledTime < _nextStaticFrameTime) { return; } _nextStaticFrameTime = Time.unscaledTime + 1f / 12f; _staticFrameIndex++; int num = _staticFrameIndex * 5 % 144; int num2 = 0; for (int i = 0; i < 144; i++) { bool flag = (i & 3) == 0; bool flag2 = Mathf.Abs(i - num) <= 3; for (int j = 0; j < 256; j++) { uint num3 = NextStaticRandom(); byte b = (byte)(24 + (num3 & 0xD7)); if (flag) { b /= 2; } if (flag2 && ((j + _staticFrameIndex * 7) & 0xF) < 12) { byte b2 = (byte)Mathf.Max(95, (int)b); _staticPixels[num2++] = new Color32(b2, (byte)(b / 10), (byte)4, byte.MaxValue); } else { _staticPixels[num2++] = new Color32(b, b, b, byte.MaxValue); } } } _staticTexture.SetPixels32(_staticPixels); _staticTexture.Apply(false, false); } private uint NextStaticRandom() { uint staticNoiseState = _staticNoiseState; staticNoiseState ^= staticNoiseState << 13; staticNoiseState ^= staticNoiseState >> 17; return _staticNoiseState = staticNoiseState ^ (staticNoiseState << 5); } private void StopStaticPlayback() { _staticPlaying = false; } private void ApplyTextureToScreen(Texture texture) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: 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) if (!((Object)(object)_screenMaterial == (Object)null) && !((Object)(object)texture == (Object)null)) { try { _screenMaterial.mainTexture = texture; } catch { } SetProp(_screenMaterial, "_BaseColorMap", texture); SetProp(_screenMaterial, "_UnlitColorMap", texture); SetProp(_screenMaterial, "_EmissiveColorMap", texture); SetProp(_screenMaterial, "_MainTex", texture); SetProp(_screenMaterial, "_BaseMap", texture); SetProp(_screenMaterial, "_EmissionMap", texture); ApplyUprightVideoTextureTransform(_screenMaterial); Color c = Color.white * 2.2f; SetColor(_screenMaterial, "_BaseColor", Color.white); SetColor(_screenMaterial, "_Color", Color.white); SetColor(_screenMaterial, "_UnlitColor", Color.white); SetColor(_screenMaterial, "_EmissiveColor", c); SetColor(_screenMaterial, "_EmissionColor", c); SetFloat(_screenMaterial, "_EmissiveIntensity", 2.2f); SetFloat(_screenMaterial, "_SurfaceType", 0f); SetFloat(_screenMaterial, "_BlendMode", 0f); SetFloat(_screenMaterial, "_AlphaClip", 0f); SetFloat(_screenMaterial, "_Cutoff", 0f); SetFloat(_screenMaterial, "_ZWrite", 1f); SetFloat(_screenMaterial, "_SrcBlend", 1f); SetFloat(_screenMaterial, "_DstBlend", 0f); ConfigureDoubleSided(_screenMaterial); _screenMaterial.EnableKeyword("_EMISSION"); _screenMaterial.EnableKeyword("_EMISSIVE_COLOR_MAP"); if ((Object)(object)_screenOverlayRenderer != (Object)null) { _screenOverlayRenderer.enabled = false; } ApplyScreenMaterialToTargets(); } } private static Material CreateRuntimeScreenMaterial(Material source) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) Shader val = Shader.Find("HDRP/Unlit") ?? Shader.Find("Unlit/Texture") ?? Shader.Find("HDRP/Lit") ?? Shader.Find("Standard") ?? Shader.Find("Diffuse"); Material val2 = (((Object)(object)val != (Object)null) ? new Material(val) : (((Object)(object)source != (Object)null) ? new Material(source) : new Material(Shader.Find("Diffuse")))); Texture val3 = (((Object)(object)source != (Object)null) ? GetFirstTexture(source) : null); if ((Object)(object)val3 != (Object)null) { try { val2.mainTexture = val3; } catch { } } ConfigureDoubleSided(val2); return val2; } private Material CreateScreenBlankMaterial() { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown if ((Object)(object)_screenBlankMaterial != (Object)null) { return _screenBlankMaterial; } Shader val = Shader.Find("HDRP/Unlit") ?? Shader.Find("Unlit/Color") ?? Shader.Find("HDRP/Lit") ?? Shader.Find("Standard") ?? Shader.Find("Diffuse"); _screenBlankMaterial = (((Object)(object)val != (Object)null) ? new Material(val) : new Material(Shader.Find("Diffuse"))); ((Object)_screenBlankMaterial).name = "FlyingTV_BlankedOriginalScreen"; Color c = default(Color); ((Color)(ref c))..ctor(0f, 0f, 0f, 1f); try { _screenBlankMaterial.mainTexture = null; } catch { } SetColor(_screenBlankMaterial, "_BaseColor", c); SetColor(_screenBlankMaterial, "_Color", c); SetColor(_screenBlankMaterial, "_UnlitColor", c); SetColor(_screenBlankMaterial, "_EmissiveColor", Color.black); SetColor(_screenBlankMaterial, "_EmissionColor", Color.black); SetFloat(_screenBlankMaterial, "_EmissiveIntensity", 0f); SetFloat(_screenBlankMaterial, "_SurfaceType", 0f); SetFloat(_screenBlankMaterial, "_BlendMode", 0f); SetFloat(_screenBlankMaterial, "_AlphaClip", 0f); SetFloat(_screenBlankMaterial, "_Cutoff", 0f); SetFloat(_screenBlankMaterial, "_ZWrite", 1f); SetFloat(_screenBlankMaterial, "_SrcBlend", 1f); SetFloat(_screenBlankMaterial, "_DstBlend", 0f); _screenBlankMaterial.DisableKeyword("_EMISSION"); _screenBlankMaterial.DisableKeyword("_EMISSIVE_COLOR_MAP"); return _screenBlankMaterial; } private void EnsureVideoDisplaySurface() { if (HasEmbeddedScreenMaterialTarget()) { if ((Object)(object)_screenOverlayRenderer != (Object)null) { _screenOverlayRenderer.enabled = false; } return; } if ((Object)(object)_screenOverlayRenderer != (Object)null) { _screenOverlayRenderer.enabled = false; } if (!_missingScreenTargetLogged) { _missingScreenTargetLogged = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[FlyingTV] No embedded TV screen material target found; refusing to create a detached runtime overlay."); } } } private void EnsureRuntimeScreenOverlay() { if ((Object)(object)_screenOverlayObject == (Object)null) { _screenOverlayObject = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)_screenOverlayObject).name = "FlyingTVRuntimeScreenOverlay"; _screenOverlayObject.transform.SetParent(((Component)this).transform, false); Collider component = _screenOverlayObject.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } _screenOverlayRenderer = _screenOverlayObject.GetComponent(); if ((Object)(object)_screenOverlayRenderer != (Object)null) { _screenOverlayRenderer.shadowCastingMode = (ShadowCastingMode)0; _screenOverlayRenderer.receiveShadows = false; _screenOverlayRenderer.enabled = true; } } else if ((Object)(object)_screenOverlayRenderer == (Object)null) { _screenOverlayRenderer = _screenOverlayObject.GetComponent(); } if ((Object)(object)_screenOverlayRenderer != (Object)null && (Object)(object)_screenMaterial != (Object)null) { _screenOverlayRenderer.sharedMaterial = _screenMaterial; } PositionRuntimeScreenOverlay(); } private void PositionRuntimeScreenOverlay() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0042: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_0092: 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_009a: 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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_005b: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: 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) if (!((Object)(object)_screenOverlayObject == (Object)null)) { Vector3 val = FlyingTVManager.GetScreenLocalFacingDirection(); val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.0025f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); Vector3 val2 = Vector3.Cross(Vector3.up, val); if (((Vector3)(ref val2)).sqrMagnitude < 0.0025f) { val2 = Vector3.right; } ((Vector3)(ref val2)).Normalize(); float num = 0.03f; float num2 = 0f; float num3 = -1.67f; float num4 = 0.44f + num; Vector3 localPosition = val2 * num2 + Vector3.up * num3 + val * num4; float num5 = 1.06f; float num6 = 0.86f; Vector3 lossyScale = ((Component)this).transform.lossyScale; float num7 = ((Mathf.Abs(lossyScale.x) > 0.001f) ? Mathf.Abs(lossyScale.x) : 1f); float num8 = ((Mathf.Abs(lossyScale.y) > 0.001f) ? Mathf.Abs(lossyScale.y) : 1f); Transform transform = _screenOverlayObject.transform; transform.localPosition = localPosition; transform.localRotation = Quaternion.LookRotation(val, Vector3.up); transform.localScale = new Vector3(num5 / num7, num6 / num8, 1f); } } private void ApplyScreenMaterialToTargets() { if ((Object)(object)_screenMaterial == (Object)null) { return; } if (_screenMaterialTargets.Count == 0) { CollectScreenMaterialTargets(); } for (int i = 0; i < _screenMaterialTargets.Count; i++) { ScreenMaterialTarget screenMaterialTarget = _screenMaterialTargets[i]; Renderer renderer = screenMaterialTarget.Renderer; if ((Object)(object)renderer == (Object)null || (Object)(object)renderer == (Object)(object)_screenOverlayRenderer) { continue; } Material[] materials = renderer.materials; if (materials != null && materials.Length != 0) { int num = Mathf.Clamp(screenMaterialTarget.MaterialIndex, 0, materials.Length - 1); materials[num] = _screenMaterial; renderer.materials = materials; if (!IsStandaloneScreenRenderer(renderer)) { renderer.enabled = true; } } } } private bool HasEmbeddedScreenMaterialTarget() { if (_screenMaterialTargets.Count == 0) { CollectScreenMaterialTargets(); } for (int i = 0; i < _screenMaterialTargets.Count; i++) { Renderer renderer = _screenMaterialTargets[i].Renderer; if ((Object)(object)renderer != (Object)null && (Object)(object)renderer != (Object)(object)_screenOverlayRenderer && !IsStandaloneScreenRenderer(renderer)) { return true; } } return false; } private void CollectScreenMaterialTargets() { _screenMaterialTargets.Clear(); List list = new List(); List list2 = new List(); Renderer[] componentsInChildren = ((Component)((Component)this).transform).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)_screenOverlayRenderer) { continue; } Material[] sharedMaterials = val.sharedMaterials; bool flag = IsStandaloneScreenRenderer(val); int num = 0; if (sharedMaterials != null) { for (int j = 0; j < sharedMaterials.Length; j++) { if (IsScreenMaterial(val, sharedMaterials[j])) { list.Add(new ScreenMaterialTarget(val, j)); num++; } } } if (num == 0 && flag) { list2.Add(new ScreenMaterialTarget(val, 0)); } } if (list.Count > 0) { _screenMaterialTargets.AddRange(list); } else { _screenMaterialTargets.AddRange(list2); } if (_screenMaterialTargets.Count > 0) { screenRenderer = _screenMaterialTargets[0].Renderer; _screenMaterialIndex = Mathf.Max(0, _screenMaterialTargets[0].MaterialIndex); } if (!_screenTargetsLogged) { _screenTargetsLogged = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[FlyingTV] Screen material targets: " + BuildScreenTargetLog())); } } } private void NeutralizeOriginalScreenSurfaces() { if (_screenMaterialTargets.Count == 0) { CollectScreenMaterialTargets(); } Material val = CreateScreenBlankMaterial(); Renderer[] componentsInChildren = ((Component)((Component)this).transform).GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren) { if ((Object)(object)val2 == (Object)null || (Object)(object)val2 == (Object)(object)_screenOverlayRenderer) { continue; } if (IsStandaloneScreenRenderer(val2)) { val2.enabled = false; continue; } Material[] materials = val2.materials; if (materials == null || materials.Length == 0) { continue; } bool flag = false; for (int j = 0; j < materials.Length; j++) { if (IsOriginalStaticMaterial(materials[j])) { materials[j] = val; flag = true; } } if (flag) { val2.materials = materials; } } } private string BuildScreenTargetLog() { if (_screenMaterialTargets.Count == 0) { return "(none)"; } List list = new List(_screenMaterialTargets.Count); for (int i = 0; i < _screenMaterialTargets.Count; i++) { ScreenMaterialTarget screenMaterialTarget = _screenMaterialTargets[i]; Renderer renderer = screenMaterialTarget.Renderer; if (!((Object)(object)renderer == (Object)null)) { Material[] sharedMaterials = renderer.sharedMaterials; string arg = ((screenMaterialTarget.MaterialIndex >= 0 && sharedMaterials != null && screenMaterialTarget.MaterialIndex < sharedMaterials.Length && (Object)(object)sharedMaterials[screenMaterialTarget.MaterialIndex] != (Object)null) ? ((Object)sharedMaterials[screenMaterialTarget.MaterialIndex]).name : ""); list.Add($"{GetHierarchyPath(((Component)renderer).transform)}[{screenMaterialTarget.MaterialIndex}]={arg}"); } } if (list.Count <= 0) { return "(none)"; } return string.Join(", ", list); } private static bool IsScreenMaterial(Renderer renderer, Material material) { if ((Object)(object)material == (Object)null) { return IsStandaloneScreenRenderer(renderer); } if (ContainsScreenToken(((Object)material).name)) { return true; } Texture firstTexture = GetFirstTexture(material); if ((Object)(object)firstTexture != (Object)null) { return ContainsScreenToken(((Object)firstTexture).name); } return false; } private static bool IsStandaloneScreenRenderer(Renderer renderer) { if ((Object)(object)renderer == (Object)null) { return false; } if (IsAuthoredEmbeddedScreenRenderer(renderer)) { return false; } string name = ((Object)renderer).name ?? string.Empty; if (!ContainsScreenToken(name)) { return false; } return !ContainsBodyToken(name); } private static bool ContainsScreenToken(string name) { if (string.IsNullOrWhiteSpace(name)) { return false; } if (name.IndexOf("screen", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("display", StringComparison.OrdinalIgnoreCase) < 0) { return name.IndexOf("crt", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static bool IsOriginalStaticMaterial(Material material) { if ((Object)(object)material == (Object)null) { return false; } string text = ((Object)material).name ?? string.Empty; if (text.IndexOf("FlyingTV_Monitor", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("silo_monitor", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } Texture firstTexture = GetFirstTexture(material); return (((Object)(object)firstTexture != (Object)null) ? (((Object)firstTexture).name ?? string.Empty) : string.Empty).IndexOf("silo_monitor", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool ContainsBodyToken(string name) { if (string.IsNullOrWhiteSpace(name)) { return false; } if (name.IndexOf("body", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("case", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("frame", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("bezel", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("propeller", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("rotor", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("blade", StringComparison.OrdinalIgnoreCase) < 0) { return name.IndexOf("antenna", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static string GetHierarchyPath(Transform transform) { if ((Object)(object)transform == (Object)null) { return ""; } List list = new List(); Transform val = transform; while ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); val = val.parent; } list.Reverse(); return string.Join("/", list); } private Bounds GetScreenOverlayBounds() { //IL_0031: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_008f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)screenRenderer != (Object)null) { return screenRenderer.bounds; } Renderer[] componentsInChildren = ((Component)((Component)this).transform).GetComponentsInChildren(true); bool flag = false; Bounds bounds = default(Bounds); ((Bounds)(ref bounds))..ctor(((Component)this).transform.position + Vector3.up * 0.15f, new Vector3(1.6f, 0.9f, 0.35f)); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)_screenOverlayRenderer)) { if (!flag) { bounds = val.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } } } return bounds; } private static float ProjectBoundsExtent(Bounds bounds, Vector3 axis) { //IL_001d: 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) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0040: 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_0053: Unknown result type (might be due to invalid IL or missing references) if (((Vector3)(ref axis)).sqrMagnitude < 0.0001f) { return 0f; } ((Vector3)(ref axis)).Normalize(); Vector3 extents = ((Bounds)(ref bounds)).extents; return Mathf.Abs(axis.x) * extents.x + Mathf.Abs(axis.y) * extents.y + Mathf.Abs(axis.z) * extents.z; } private static Texture GetFirstTexture(Material material) { if ((Object)(object)material == (Object)null) { return null; } Texture val = null; try { val = material.mainTexture; } catch { } return val ?? GetTextureIfPresent(material, "_BaseColorMap") ?? GetTextureIfPresent(material, "_UnlitColorMap") ?? GetTextureIfPresent(material, "_EmissiveColorMap") ?? GetTextureIfPresent(material, "_MainTex") ?? GetTextureIfPresent(material, "_BaseMap"); } private static int ResolveScreenMaterialIndex(Material[] materials, int preferredIndex) { if (materials == null || materials.Length == 0) { return 0; } if (preferredIndex >= 0 && preferredIndex < materials.Length) { return preferredIndex; } for (int i = 0; i < materials.Length; i++) { string text = (((Object)(object)materials[i] != (Object)null) ? ((Object)materials[i]).name : string.Empty); if (text.IndexOf("FlyingTV_Screen", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Screen", StringComparison.OrdinalIgnoreCase) >= 0) { return i; } } return 0; } private static Texture2D BuildFallbackTexture() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_002b: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(256, 144, (TextureFormat)4, false) { name = "FlyingTVFallback", wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)0 }; Color val2 = default(Color); ((Color)(ref val2))..ctor(0.015f, 0.018f, 0.02f, 1f); Color val3 = default(Color); ((Color)(ref val3))..ctor(0.85f, 0.05f, 0.03f, 1f); Color val4 = default(Color); ((Color)(ref val4))..ctor(0.82f, 0.88f, 0.9f, 1f); for (int i = 0; i < 144; i++) { for (int j = 0; j < 256; j++) { bool num = i % 4 == 0; bool flag = ((j * 19 + i * 47) & 0x1F) < 3; Color val5 = ((i > 62 && i < 82) ? val3 : (flag ? val4 : val2)); if (num) { val5 *= 0.55f; } val.SetPixel(j, i, val5); } } val.Apply(false, false); return val; } private string ResolveVideoLocation(string videoName) { if (!string.IsNullOrWhiteSpace(videoName)) { string text = videoName.Trim(); if (IsVideoUrl(text)) { return text; } if (Path.IsPathRooted(text)) { return text; } string text2 = Path.Combine(Plugin.AssemblyDirectory, text); if (File.Exists(text2)) { return text2; } string text3 = Path.Combine(GetVideoDir(), text); File.Exists(text3); return text3; } string videoDir = GetVideoDir(); if (!Directory.Exists(videoDir)) { return null; } string[] videoExtensions = VideoExtensions; foreach (string text4 in videoExtensions) { string[] files = Directory.GetFiles(videoDir, "*" + text4, SearchOption.TopDirectoryOnly); if (files.Length != 0) { Array.Sort(files, (IComparer?)StringComparer.OrdinalIgnoreCase); return files[0]; } } return null; } private string GetVideoDir() { string text = ((FlyingTVConfig.VideoDirectory != null) ? FlyingTVConfig.VideoDirectory.Value : "FlyingTVVideos"); if (Path.IsPathRooted(text)) { return text; } return Path.Combine(Plugin.AssemblyDirectory ?? string.Empty, text); } private static bool IsVideoUrl(string value) { if (string.IsNullOrWhiteSpace(value)) { return false; } if (!Uri.TryCreate(value, UriKind.Absolute, out Uri result)) { return false; } if (!(result.Scheme == Uri.UriSchemeHttp) && !(result.Scheme == Uri.UriSchemeHttps)) { return result.Scheme == Uri.UriSchemeFile; } return true; } private void ResolveReferences() { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_0116: Unknown result type (might be due to invalid IL or missing references) EnsureCollisionBody(); if ((Object)(object)visualRoot == (Object)null) { Transform val = ((Component)this).transform.Find("VisualRoot"); if ((Object)(object)val != (Object)null) { visualRoot = val; } } if ((Object)(object)base.creatureAnimator == (Object)null) { base.creatureAnimator = ((Component)this).GetComponentInChildren(true); } if ((Object)(object)screenRenderer == (Object)null || FindNamedScreenMaterialIndex(screenRenderer.sharedMaterials) < 0) { Renderer val2 = FindBestScreenRenderer(((Component)this).transform, out _screenMaterialIndex); if ((Object)(object)val2 != (Object)null) { screenRenderer = val2; } } else { _screenMaterialIndex = ResolveScreenMaterialIndex(screenRenderer.sharedMaterials, _screenMaterialIndex); } if ((Object)(object)videoAudioSource == (Object)null) { Transform val3 = Plugin.FindChildRecursive(((Component)this).transform, "VideoAudio"); if ((Object)(object)val3 != (Object)null) { videoAudioSource = ((Component)val3).GetComponent(); } } if ((Object)(object)videoAudioSource == (Object)null) { GameObject val4 = new GameObject("VideoAudio"); val4.transform.SetParent(((Component)this).transform, false); val4.transform.localPosition = Vector3.zero; videoAudioSource = val4.AddComponent(); } ConfigureAudioSource(videoAudioSource); _propellerSource = EnsureAudioSource(_propellerSource, "FlyingTVPropellerLoop", 38f); if ((Object)(object)greetingsClip == (Object)null) { greetingsClip = Plugin.GreetingsClip; } if ((Object)(object)noncomplianceClip == (Object)null) { noncomplianceClip = Plugin.NoncomplianceClip; } if ((Object)(object)complianceClip == (Object)null) { complianceClip = Plugin.ComplianceClip; } if ((Object)(object)propellerLoopClip == (Object)null) { propellerLoopClip = Plugin.PropellerClip; } if ((Object)(object)base.creatureAnimator != (Object)null) { base.creatureAnimator.applyRootMotion = false; base.creatureAnimator.cullingMode = (AnimatorCullingMode)0; _hasMovingParameter = HasParam(base.creatureAnimator, MovingHash, (AnimatorControllerParameterType)4); } } private void EnsureBehaviourStates() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_0061: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown //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) //IL_009d: 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) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: 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_00e3: Expected O, but got Unknown //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Expected O, but got Unknown base.enemyBehaviourStates = (EnemyBehaviourState[])(object)new EnemyBehaviourState[7] { new EnemyBehaviourState { name = "Roaming", IsAnimTrigger = false, parameterString = "Moving", boolValue = true }, new EnemyBehaviourState { name = "Approach", IsAnimTrigger = false, parameterString = "Moving", boolValue = true }, new EnemyBehaviourState { name = "Checking", IsAnimTrigger = false, parameterString = "Moving", boolValue = false }, new EnemyBehaviourState { name = "Video", IsAnimTrigger = false, parameterString = "Moving", boolValue = false }, new EnemyBehaviourState { name = "Attack", IsAnimTrigger = false, parameterString = "Moving", boolValue = true }, new EnemyBehaviourState { name = "Leave", IsAnimTrigger = false, parameterString = "Moving", boolValue = true }, new EnemyBehaviourState { name = "Dead", IsAnimTrigger = true, parameterString = "Death" } }; } private AudioSource EnsureAudioSource(AudioSource existing, string name, float maxDistance) { //IL_0022: 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) AudioSource val = existing; if ((Object)(object)val == (Object)null) { Transform val2 = ((Component)this).transform.Find(name); if ((Object)(object)val2 == (Object)null) { val2 = new GameObject(name).transform; val2.SetParent(((Component)this).transform, false); val2.localPosition = Vector3.zero; } val = ((Component)val2).GetComponent() ?? ((Component)val2).gameObject.AddComponent(); } ConfigureAudioSource(val, maxDistance); return val; } private static void ConfigureAudioSource(AudioSource source, float maxDistance = 34f) { if (!((Object)(object)source == (Object)null)) { source.playOnAwake = false; source.spatialize = false; source.spatializePostEffects = false; source.spatialBlend = 1f; source.minDistance = 2f; source.maxDistance = maxDistance; source.rolloffMode = (AudioRolloffMode)0; } } private static bool HasParam(Animator anim, int hash, AnimatorControllerParameterType type) { //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) if ((Object)(object)anim == (Object)null) { return false; } AnimatorControllerParameter[] parameters = anim.parameters; foreach (AnimatorControllerParameter val in parameters) { if (val.nameHash == hash && val.type == type) { return true; } } return false; } private static void SetProp(Material m, string prop, Texture tex) { if ((Object)(object)m != (Object)null && (Object)(object)tex != (Object)null && m.HasProperty(prop)) { m.SetTexture(prop, tex); } } private static void ApplyUprightVideoTextureTransform(Material material) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)material == (Object)null)) { try { material.mainTextureScale = new Vector2(1f, -1f); material.mainTextureOffset = new Vector2(0f, 1f); } catch { } SetTextureTransform(material, "_BaseColorMap"); SetTextureTransform(material, "_UnlitColorMap"); SetTextureTransform(material, "_EmissiveColorMap"); SetTextureTransform(material, "_MainTex"); SetTextureTransform(material, "_BaseMap"); SetTextureTransform(material, "_EmissionMap"); } } private static void SetTextureTransform(Material material, string property) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)material == (Object)null) && material.HasProperty(property)) { material.SetTextureScale(property, new Vector2(1f, -1f)); material.SetTextureOffset(property, new Vector2(0f, 1f)); } } private static void SetColor(Material m, string prop, Color c) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)m != (Object)null && m.HasProperty(prop)) { m.SetColor(prop, c); } } private static void SetFloat(Material m, string prop, float value) { if ((Object)(object)m != (Object)null && m.HasProperty(prop)) { m.SetFloat(prop, value); } } private static void ConfigureDoubleSided(Material material) { SetFloat(material, "_Cull", 0f); SetFloat(material, "_CullMode", 0f); SetFloat(material, "_CullModeForward", 0f); SetFloat(material, "_TransparentCullMode", 0f); SetFloat(material, "_DoubleSidedEnable", 1f); if (material != null) { material.EnableKeyword("_DOUBLESIDED_ON"); } } private static Texture GetTextureIfPresent(Material material, string prop) { if (!((Object)(object)material != (Object)null) || !material.HasProperty(prop)) { return null; } return material.GetTexture(prop); } private static Renderer FindBestScreenRenderer(Transform root, out int materialIndex) { materialIndex = -1; if ((Object)(object)root == (Object)null) { return null; } Renderer result = null; int num = int.MinValue; int num2 = -1; Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } int num3 = FindNamedScreenMaterialIndex(val.sharedMaterials); string text = ((Object)val).name ?? string.Empty; bool flag = IsAuthoredEmbeddedScreenRenderer(val); bool flag2 = text.IndexOf("Screen", StringComparison.OrdinalIgnoreCase) >= 0; bool flag3 = text.IndexOf("TV", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Monitor", StringComparison.OrdinalIgnoreCase) >= 0; if (num3 >= 0 || flag2 || flag3) { int num4 = 0; if (flag) { num4 += 1000; } if (num3 >= 0) { num4 += 100; } if (flag3) { num4 += 30; } if (flag2) { num4 += 20; } if (!val.enabled) { num4 -= 5; } if (num4 > num) { result = val; num = num4; num2 = ((num3 >= 0) ? num3 : 0); } } } materialIndex = Mathf.Max(0, num2); return result; } private static int FindNamedScreenMaterialIndex(Material[] materials) { if (materials == null) { return -1; } for (int i = 0; i < materials.Length; i++) { string text = (((Object)(object)materials[i] != (Object)null) ? ((Object)materials[i]).name : string.Empty); if (text.IndexOf("FlyingTV_Screen", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Screen", StringComparison.OrdinalIgnoreCase) >= 0) { return i; } } return -1; } private static bool IsAuthoredEmbeddedScreenRenderer(Renderer renderer) { if ((Object)(object)renderer != (Object)null) { return ((Object)renderer).name.IndexOf("FlyingTVEmbeddedScreenSurface", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } protected override void __initializeVariables() { ((EnemyAI)this).__initializeVariables(); } protected override void __initializeRpcs() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown ((NetworkBehaviour)this).__registerRpc(393840246u, new RpcReceiveHandler(__rpc_handler_393840246), "DeathExplosionClientRpc"); ((NetworkBehaviour)this).__registerRpc(2863110160u, new RpcReceiveHandler(__rpc_handler_2863110160), "ReportAttackHitServerRpc"); ((NetworkBehaviour)this).__registerRpc(3566271186u, new RpcReceiveHandler(__rpc_handler_3566271186), "ApplyAttackDamageClientRpc"); ((NetworkBehaviour)this).__registerRpc(2130784982u, new RpcReceiveHandler(__rpc_handler_2130784982), "PrefetchYoutubePlaybackClientRpc"); ((NetworkBehaviour)this).__registerRpc(3395646845u, new RpcReceiveHandler(__rpc_handler_3395646845), "StartPlaybackClientRpc"); ((NetworkBehaviour)this).__registerRpc(2066075155u, new RpcReceiveHandler(__rpc_handler_2066075155), "StopPlaybackClientRpc"); ((NetworkBehaviour)this).__registerRpc(2905851093u, new RpcReceiveHandler(__rpc_handler_2905851093), "SelectedPlaybackFinishedServerRpc"); ((EnemyAI)this).__initializeRpcs(); } private static void __rpc_handler_393840246(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0036: 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_0050: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { Vector3 position = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref position); target.__rpc_exec_stage = (__RpcExecStage)1; ((FlyingTVAi)(object)target).DeathExplosionClientRpc(position); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_2863110160(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0023: 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_0050: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { ulong playerClientId = default(ulong); ByteUnpacker.ReadValueBitPacked(reader, ref playerClientId); target.__rpc_exec_stage = (__RpcExecStage)1; ((FlyingTVAi)(object)target).ReportAttackHitServerRpc(playerClientId); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_3566271186(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { ulong targetClientId = default(ulong); ByteUnpacker.ReadValueBitPacked(reader, ref targetClientId); int damage = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref damage); target.__rpc_exec_stage = (__RpcExecStage)1; ((FlyingTVAi)(object)target).ApplyAttackDamageClientRpc(targetClientId, damage); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_2130784982(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //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_0061: 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) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); string youtubeUrl = null; if (flag) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref youtubeUrl, false); } target.__rpc_exec_stage = (__RpcExecStage)1; ((FlyingTVAi)(object)target).PrefetchYoutubePlaybackClientRpc(youtubeUrl); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_3395646845(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_0088: 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_00b9: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); string videoName = null; if (flag) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref videoName, false); } float durationSeconds = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe(ref durationSeconds, default(ForPrimitives)); double playbackStartServerTime = default(double); ((FastBufferReader)(ref reader)).ReadValueSafe(ref playbackStartServerTime, default(ForPrimitives)); target.__rpc_exec_stage = (__RpcExecStage)1; ((FlyingTVAi)(object)target).StartPlaybackClientRpc(videoName, durationSeconds, playbackStartServerTime); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_2066075155(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { target.__rpc_exec_stage = (__RpcExecStage)1; ((FlyingTVAi)(object)target).StopPlaybackClientRpc(); target.__rpc_exec_stage = (__RpcExecStage)0; } } private static void __rpc_handler_2905851093(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) NetworkManager networkManager = target.NetworkManager; if (networkManager != null && networkManager.IsListening) { ServerRpcParams server = rpcParams.Server; target.__rpc_exec_stage = (__RpcExecStage)1; ((FlyingTVAi)(object)target).SelectedPlaybackFinishedServerRpc(server); target.__rpc_exec_stage = (__RpcExecStage)0; } } protected internal override string __getTypeName() { return "FlyingTVAi"; } } public static class FlyingTVApi { public static int SpawnForAllLivingPlayers(string videoName = null, float durationSeconds = -1f) { return FlyingTVManager.SpawnForAllLivingPlayers(videoName, durationSeconds); } public static bool SpawnForPlayer(PlayerControllerB player, string videoName = null, float durationSeconds = -1f) { return FlyingTVManager.SpawnForPlayer(player, videoName, durationSeconds); } } internal static class FlyingTVConfig { internal static ConfigEntry Enabled; internal static ConfigEntry SpawnWeightMultiplier; internal static ConfigEntry MaxCount; internal static ConfigEntry PowerLevel; internal static ConfigEntry VideoDirectory; internal static ConfigEntry VideoFileName; internal static ConfigEntry YoutubeVideoLink; internal static ConfigEntry PlaybackDurationSeconds; internal static ConfigEntry VisibleOnlyToTarget; internal static ConfigEntry FollowDistance; internal static ConfigEntry VideoNoncomplianceDistance; internal static ConfigEntry TurnAwayAngleThreshold; internal static ConfigEntry VideoNoncomplianceSustainSeconds; internal static ConfigEntry DetectionRange; internal static ConfigEntry DetectionConeDegrees; internal static ConfigEntry RoamSpeed; internal static ConfigEntry ApproachSpeed; internal static ConfigEntry ChaseSpeed; internal static ConfigEntry PropellerVolume; internal static ConfigEntry DebugSpawnKey; internal static void Bind(ConfigFile config) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Expected O, but got Unknown //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Expected O, but got Unknown //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Expected O, but got Unknown //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Expected O, but got Unknown //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Expected O, but got Unknown //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Expected O, but got Unknown //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Expected O, but got Unknown //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Expected O, but got Unknown //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Expected O, but got Unknown //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Expected O, but got Unknown //IL_03ad: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Expected O, but got Unknown //IL_03d1: Unknown result type (might be due to invalid IL or missing references) bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; try { FlyingTVConfigMigration.TryMigrate(config, Plugin.Log); ConfigSectionNameMigration.TryMigrate(config, Plugin.Log, "Warden"); Enabled = config.Bind("General", "Enabled", true, "Enable the Warden encounter and its host-only debug spawn hook."); SpawnWeightMultiplier = config.Bind("Spawning", "SpawnWeightMultiplier", 1f, new ConfigDescription("Multiplier applied to Warden's contextual moon/interior spawn policy. 0 disables natural spawns.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 5f), Array.Empty())); MaxCount = config.Bind("Spawning", "MaxCount", 3, new ConfigDescription("Maximum number of living Wardens at once.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 8), Array.Empty())); PowerLevel = config.Bind("Spawning", "PowerLevel", 2, new ConfigDescription("Indoor enemy power budget cost for each Warden.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 20), Array.Empty())); VideoDirectory = config.Bind("Video", "VideoDirectory", "FlyingTVVideos", "Directory beside Y4NGZFlyingTV.dll to search when VideoFileName is relative or empty."); VideoFileName = config.Bind("Video", "VideoFileName", string.Empty, "Fallback video filename, absolute path, direct media URL, or YouTube video link. Used only when YouTube.VideoLink is empty; empty selects the first supported file in VideoDirectory."); PlaybackDurationSeconds = config.Bind("Video", "PlaybackDurationSeconds", 0f, new ConfigDescription("Optional forced playback duration. 0 lets the client video's finish event control departure.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 600f), Array.Empty())); YoutubeVideoLink = config.Bind("YouTube", "VideoLink", string.Empty, "Full YouTube video link. When non-empty, this takes priority over VideoFileName and video sources supplied through the API."); VisibleOnlyToTarget = config.Bind("Encounter Rules", "VisibleOnlyToTarget", true, "When enabled, each Warden is rendered and heard only by its current target."); FollowDistance = config.Bind("Encounter Rules", "FollowDistance", 2f, new ConfigDescription("Distance from its target where Warden stops approaching and asks them to hold still.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 8f), Array.Empty())); VideoNoncomplianceDistance = config.Bind("Encounter Rules", "VideoNoncomplianceDistance", 10f, new ConfigDescription("Maximum distance from Warden during playback. Moving farther away independently counts as noncompliance after the five-second grace period.", (AcceptableValueBase)(object)new AcceptableValueRange(2f, 20f), Array.Empty())); TurnAwayAngleThreshold = config.Bind("Encounter Rules", "TurnAwayAngleThreshold", 80f, new ConfigDescription("Maximum angle between the target's view direction and Warden's screen. Looking outside this cone independently counts as noncompliance after the five-second grace period.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 180f), Array.Empty())); VideoNoncomplianceSustainSeconds = config.Bind("Encounter Rules", "VideoNoncomplianceSustainSeconds", 1f, new ConfigDescription("Continuous time the target must remain outside the distance limit or view cone before Warden attacks.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty())); UpgradeLegacyNoncomplianceDefaults(); DetectionRange = config.Bind("Encounter Rules", "DetectionRange", 40f, new ConfigDescription("Maximum line-of-sight distance at which Warden can notice a player.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 100f), Array.Empty())); DetectionConeDegrees = config.Bind("Encounter Rules", "DetectionConeDegrees", 120f, new ConfigDescription("Full width of Warden's screen-facing vision cone in degrees.", (AcceptableValueBase)(object)new AcceptableValueRange(10f, 360f), Array.Empty())); RoamSpeed = config.Bind("Movement", "RoamSpeed", 3.2f, new ConfigDescription("Movement speed while searching the facility.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 10f), Array.Empty())); ApproachSpeed = config.Bind("Movement", "ApproachSpeed", 6.5f, new ConfigDescription("Movement speed while approaching a player to present a video.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 15f), Array.Empty())); ChaseSpeed = config.Bind("Movement", "ChaseSpeed", 6.2f, new ConfigDescription("Movement speed while chasing a noncompliant player.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 15f), Array.Empty())); PropellerVolume = config.Bind("Audio", "PropellerVolume", 0.65f, new ConfigDescription("Volume of Warden's passive propeller loop.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); DebugSpawnKey = config.Bind("Diagnostics", "SpawnForLivingPlayersKey", new KeyboardShortcut((KeyCode)291, Array.Empty()), "Host-only debug key that spawns one Warden event for every living player."); } finally { config.SaveOnConfigSet = saveOnConfigSet; if (saveOnConfigSet) { config.Save(); } } } private static void UpgradeLegacyNoncomplianceDefaults() { if (VideoNoncomplianceDistance != null && Mathf.Approximately(VideoNoncomplianceDistance.Value, 6f)) { VideoNoncomplianceDistance.Value = 10f; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[Warden] Updated the legacy noncompliance distance default from 6m to 10m."); } } if (TurnAwayAngleThreshold != null && Mathf.Approximately(TurnAwayAngleThreshold.Value, 165f)) { TurnAwayAngleThreshold.Value = 80f; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[Warden] Updated the legacy turn-away threshold default from 165 to 80 degrees."); } } } internal static string ResolveVideoSource(string requestedVideo = null) { string text = YoutubeVideoLink?.Value?.Trim(); if (!string.IsNullOrWhiteSpace(text)) { return text; } if (!string.IsNullOrWhiteSpace(requestedVideo)) { return requestedVideo.Trim(); } return VideoFileName?.Value?.Trim() ?? string.Empty; } } internal static class FlyingTVConfigMigration { private static readonly ConfigDefinition LegacySpawnWeight = new ConfigDefinition("Spawning", "SpawnWeight"); private static readonly ConfigDefinition[] RetainedLegacyDefinitions = (ConfigDefinition[])(object)new ConfigDefinition[19] { new ConfigDefinition("General", "Enabled"), new ConfigDefinition("Spawning", "MaxCount"), new ConfigDefinition("Spawning", "PowerLevel"), new ConfigDefinition("Video", "VideoDirectory"), new ConfigDefinition("Video", "VideoFileName"), new ConfigDefinition("YouTube", "VideoLink"), new ConfigDefinition("Video", "PlaybackDurationSeconds"), new ConfigDefinition("Video", "VideoNoncomplianceDistance"), new ConfigDefinition("Video", "TurnAwayAngleThreshold"), new ConfigDefinition("Video", "VideoNoncomplianceSustainSeconds"), new ConfigDefinition("Visibility", "VisibleOnlyToTarget"), new ConfigDefinition("Movement", "FollowDistance"), new ConfigDefinition("Movement", "ApproachSpeed"), new ConfigDefinition("Movement", "DetectionRange"), new ConfigDefinition("Movement", "DetectionConeDegrees"), new ConfigDefinition("Movement", "RoamSpeed"), new ConfigDefinition("Movement", "ChaseSpeed"), new ConfigDefinition("Audio", "PropellerVolume"), new ConfigDefinition("Debug", "SpawnForLivingPlayersKey") }; private static readonly Dictionary RemovedDefaults = new Dictionary { { new ConfigDefinition("Spawning", "SpawnLevelMask"), "-1" }, { new ConfigDefinition("Spawning", "SpawnType"), "Default" }, { new ConfigDefinition("Video", "FallbackDurationSeconds"), "10" }, { new ConfigDefinition("Video", "MaximumUnknownDurationSeconds"), "300" }, { new ConfigDefinition("Movement", "SpawnDistance"), "24" }, { new ConfigDefinition("Movement", "SpawnHeight"), "7" }, { new ConfigDefinition("Movement", "SpawnSideOffset"), "6" }, { new ConfigDefinition("Movement", "FollowHeight"), "2.4" }, { new ConfigDefinition("Movement", "PlayerStopWindowSeconds"), "1" }, { new ConfigDefinition("Movement", "PlayerStopThreshold"), "0.35" }, { new ConfigDefinition("Movement", "LeaveSpeed"), "14" }, { new ConfigDefinition("Movement", "LeaveSeconds"), "3" }, { new ConfigDefinition("Movement", "ProximityAwarenessRange"), "3" }, { new ConfigDefinition("Movement", "HoverHeight"), "1.9" }, { new ConfigDefinition("Movement", "AgentAcceleration"), "10" }, { new ConfigDefinition("Movement", "ChaseLoseAggroDistance"), "28" }, { new ConfigDefinition("Movement", "ChaseLoseAggroSeconds"), "4" }, { new ConfigDefinition("Movement", "ApproachLoseAggroDistance"), "45" }, { new ConfigDefinition("Movement", "ApproachLoseAggroSeconds"), "8" }, { new ConfigDefinition("Screen", "RenderTextureWidth"), "1024" }, { new ConfigDefinition("Screen", "RenderTextureHeight"), "576" }, { new ConfigDefinition("Screen", "ScreenEmission"), "2.2" }, { new ConfigDefinition("Screen", "ScreenFacingYawOffsetDegrees"), "0" }, { new ConfigDefinition("Screen", "ScreenOverlayDepthOffset"), "0.03" }, { new ConfigDefinition("Screen", "ScreenPanelLocalX"), "0" }, { new ConfigDefinition("Screen", "ScreenPanelLocalY"), "-1.67" }, { new ConfigDefinition("Screen", "ScreenPanelLocalDepth"), "0.44" }, { new ConfigDefinition("Screen", "ScreenPanelWidth"), "1.06" }, { new ConfigDefinition("Screen", "ScreenPanelHeight"), "0.86" } }; internal static bool TryMigrate(ConfigFile config, ManualLogSource log) { string configFilePath = config.ConfigFilePath; if (!File.Exists(configFilePath)) { return false; } Dictionary dictionary = null; string text = null; try { dictionary = ReadValues(configFilePath); if (!ContainsLegacyConfig(dictionary)) { return false; } text = GetBackupPath(configFilePath); File.Copy(configFilePath, text, overwrite: false); File.WriteAllText(configFilePath, BuildMigratedConfig(dictionary), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); config.Reload(); } catch (Exception ex) { string text2 = "The original config was not modified."; if (!string.IsNullOrEmpty(text) && File.Exists(text)) { try { File.Copy(text, configFilePath, overwrite: true); config.Reload(); text2 = "The original config was restored from its backup."; } catch (Exception ex2) { text2 = "Automatic restore also failed: " + ex2.GetBaseException().Message + ". Recover manually from " + text + "."; } } if (log != null) { log.LogWarning((object)("[FlyingTV] Could not migrate legacy config: " + ex.GetBaseException().Message + " " + text2)); } return false; } List list = FindCustomizedDroppedKeys(dictionary); if (log != null) { log.LogInfo((object)("[FlyingTV] Migrated legacy config to the compact DawnLib layout. Backup: " + text)); } if (list.Count > 0 && log != null) { log.LogWarning((object)("[FlyingTV] These customized legacy settings are now authored source tuning and were not carried forward: " + string.Join(", ", list))); } return true; } private static bool ContainsLegacyConfig(Dictionary values) { if (values.ContainsKey(LegacySpawnWeight)) { return true; } for (int i = 0; i < RetainedLegacyDefinitions.Length; i++) { if (values.ContainsKey(RetainedLegacyDefinitions[i])) { return true; } } foreach (ConfigDefinition key in RemovedDefaults.Keys) { if (values.ContainsKey(key)) { return true; } } return false; } private static string BuildMigratedConfig(Dictionary values) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown //IL_00bc: Expected O, but got Unknown //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown //IL_0119: Expected O, but got Unknown //IL_0135: 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_0153: Expected O, but got Unknown //IL_0153: Expected O, but got Unknown //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_019e: Expected O, but got Unknown //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Expected O, but got Unknown //IL_01d8: Expected O, but got Unknown //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Expected O, but got Unknown //IL_0212: Expected O, but got Unknown //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Expected O, but got Unknown //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Expected O, but got Unknown //IL_029a: Expected O, but got Unknown //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Expected O, but got Unknown //IL_02d4: Expected O, but got Unknown //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Expected O, but got Unknown //IL_030e: Expected O, but got Unknown //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Expected O, but got Unknown //IL_0348: Expected O, but got Unknown //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Expected O, but got Unknown //IL_0382: Expected O, but got Unknown //IL_039e: Unknown result type (might be due to invalid IL or missing references) //IL_03ad: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Expected O, but got Unknown //IL_03bc: Expected O, but got Unknown //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_03e7: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Expected O, but got Unknown //IL_03f6: Expected O, but got Unknown //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_0432: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Expected O, but got Unknown //IL_0441: Expected O, but got Unknown //IL_045d: Unknown result type (might be due to invalid IL or missing references) //IL_046c: Unknown result type (might be due to invalid IL or missing references) //IL_047b: Expected O, but got Unknown //IL_047b: Expected O, but got Unknown //IL_0497: Unknown result type (might be due to invalid IL or missing references) //IL_04a6: Unknown result type (might be due to invalid IL or missing references) //IL_04b5: Expected O, but got Unknown //IL_04b5: Expected O, but got Unknown //IL_04e2: Unknown result type (might be due to invalid IL or missing references) //IL_04f1: Unknown result type (might be due to invalid IL or missing references) //IL_0500: Expected O, but got Unknown //IL_0500: Expected O, but got Unknown //IL_052d: Unknown result type (might be due to invalid IL or missing references) //IL_053c: Unknown result type (might be due to invalid IL or missing references) //IL_054b: Expected O, but got Unknown //IL_054b: Expected O, but got Unknown string text = GetValue(values, new ConfigDefinition("10 - Spawning", "SpawnWeightMultiplier"), null, null); if (string.IsNullOrWhiteSpace(text)) { text = ((!float.TryParse(GetValue(values, LegacySpawnWeight, null, "25"), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) ? "1" : Math.Max(0f, Math.Min(5f, result / 25f)).ToString("0.###", CultureInfo.InvariantCulture)); } StringBuilder stringBuilder = new StringBuilder(); AppendSection(stringBuilder, "00 - General", ("Enabled", GetValue(values, new ConfigDefinition("00 - General", "Enabled"), new ConfigDefinition("General", "Enabled"), "true"))); AppendSection(stringBuilder, "10 - Spawning", ("SpawnWeightMultiplier", text), ("MaxCount", GetValue(values, new ConfigDefinition("10 - Spawning", "MaxCount"), new ConfigDefinition("Spawning", "MaxCount"), "3")), ("PowerLevel", GetValue(values, new ConfigDefinition("10 - Spawning", "PowerLevel"), new ConfigDefinition("Spawning", "PowerLevel"), "2"))); AppendSection(stringBuilder, "20 - Video", ("VideoDirectory", GetValue(values, new ConfigDefinition("20 - Video", "VideoDirectory"), new ConfigDefinition("Video", "VideoDirectory"), "FlyingTVVideos")), ("VideoFileName", GetValue(values, new ConfigDefinition("20 - Video", "VideoFileName"), new ConfigDefinition("Video", "VideoFileName"), string.Empty)), ("PlaybackDurationSeconds", GetValue(values, new ConfigDefinition("20 - Video", "PlaybackDurationSeconds"), new ConfigDefinition("Video", "PlaybackDurationSeconds"), "0"))); AppendSection(stringBuilder, "YouTube", ("VideoLink", GetValue(values, new ConfigDefinition("YouTube", "VideoLink"), null, string.Empty))); AppendSection(stringBuilder, "30 - Encounter Rules", ("VisibleOnlyToTarget", GetValue(values, new ConfigDefinition("30 - Encounter Rules", "VisibleOnlyToTarget"), new ConfigDefinition("Visibility", "VisibleOnlyToTarget"), "true")), ("FollowDistance", GetValue(values, new ConfigDefinition("30 - Encounter Rules", "FollowDistance"), new ConfigDefinition("Movement", "FollowDistance"), "2")), ("VideoNoncomplianceDistance", GetValue(values, new ConfigDefinition("30 - Encounter Rules", "VideoNoncomplianceDistance"), new ConfigDefinition("Video", "VideoNoncomplianceDistance"), "10")), ("TurnAwayAngleThreshold", GetValue(values, new ConfigDefinition("30 - Encounter Rules", "TurnAwayAngleThreshold"), new ConfigDefinition("Video", "TurnAwayAngleThreshold"), "80")), ("VideoNoncomplianceSustainSeconds", GetValue(values, new ConfigDefinition("30 - Encounter Rules", "VideoNoncomplianceSustainSeconds"), new ConfigDefinition("Video", "VideoNoncomplianceSustainSeconds"), "1")), ("DetectionRange", GetValue(values, new ConfigDefinition("30 - Encounter Rules", "DetectionRange"), new ConfigDefinition("Movement", "DetectionRange"), "40")), ("DetectionConeDegrees", GetValue(values, new ConfigDefinition("30 - Encounter Rules", "DetectionConeDegrees"), new ConfigDefinition("Movement", "DetectionConeDegrees"), "120"))); AppendSection(stringBuilder, "40 - Movement", ("RoamSpeed", GetValue(values, new ConfigDefinition("40 - Movement", "RoamSpeed"), new ConfigDefinition("Movement", "RoamSpeed"), "3.2")), ("ApproachSpeed", GetValue(values, new ConfigDefinition("40 - Movement", "ApproachSpeed"), new ConfigDefinition("Movement", "ApproachSpeed"), "6.5")), ("ChaseSpeed", GetValue(values, new ConfigDefinition("40 - Movement", "ChaseSpeed"), new ConfigDefinition("Movement", "ChaseSpeed"), "6.2"))); AppendSection(stringBuilder, "60 - Audio", ("PropellerVolume", GetValue(values, new ConfigDefinition("60 - Audio", "PropellerVolume"), new ConfigDefinition("Audio", "PropellerVolume"), "0.65"))); AppendSection(stringBuilder, "99 - Diagnostics", ("SpawnForLivingPlayersKey", GetValue(values, new ConfigDefinition("99 - Diagnostics", "SpawnForLivingPlayersKey"), new ConfigDefinition("Debug", "SpawnForLivingPlayersKey"), "F10"))); return stringBuilder.ToString(); } private static void AppendSection(StringBuilder output, string section, params (string Key, string Value)[] entries) { output.Append('[').Append(section).AppendLine("]"); for (int i = 0; i < entries.Length; i++) { output.Append(entries[i].Key).Append(" = ").AppendLine(entries[i].Value); } output.AppendLine(); } private static string GetValue(Dictionary values, ConfigDefinition preferred, ConfigDefinition legacy, string fallback) { if (preferred != (ConfigDefinition)null && values.TryGetValue(preferred, out var value)) { return value; } if (legacy != (ConfigDefinition)null && values.TryGetValue(legacy, out value)) { return value; } return fallback; } private static Dictionary ReadValues(string path) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown Dictionary dictionary = new Dictionary(); string text = string.Empty; string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2.StartsWith("#", StringComparison.Ordinal)) { continue; } if (text2.StartsWith("[", StringComparison.Ordinal) && text2.EndsWith("]", StringComparison.Ordinal)) { text = text2.Substring(1, text2.Length - 2); continue; } int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); string value = text2.Substring(num + 1).Trim(); dictionary[new ConfigDefinition(text, text3)] = value; } } return dictionary; } private static List FindCustomizedDroppedKeys(Dictionary values) { List list = new List(); foreach (KeyValuePair removedDefault in RemovedDefaults) { if (values.TryGetValue(removedDefault.Key, out var value) && !ValuesEquivalent(value, removedDefault.Value)) { list.Add(removedDefault.Key.Section + "." + removedDefault.Key.Key); } } list.Sort(StringComparer.OrdinalIgnoreCase); return list; } private static bool ValuesEquivalent(string left, string right) { if (string.Equals(left.Trim(), right.Trim(), StringComparison.OrdinalIgnoreCase)) { return true; } if (float.TryParse(left, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(right, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return Math.Abs(result - result2) < 0.0001f; } return false; } private static string GetBackupPath(string configPath) { string text = configPath + ".pre-dawn.bak"; if (!File.Exists(text)) { return text; } for (int i = 2; i < 1000; i++) { string text2 = configPath + $".pre-dawn.{i}.bak"; if (!File.Exists(text2)) { return text2; } } throw new IOException("Could not allocate a Flying TV config backup filename."); } } internal sealed class FlyingTVManager : MonoBehaviour { private static FlyingTVManager _instance; private float _debugCooldownUntil; internal static void EnsureInstance() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown if (!((Object)(object)_instance != (Object)null)) { FlyingTVManager flyingTVManager = Object.FindObjectOfType(); if ((Object)(object)flyingTVManager != (Object)null) { _instance = flyingTVManager; return; } GameObject val = new GameObject("Y4NGZFlyingTVManager"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); } } private void Update() { //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) if (FlyingTVConfig.Enabled == null || !FlyingTVConfig.Enabled.Value || FlyingTVConfig.DebugSpawnKey == null) { return; } KeyboardShortcut value = FlyingTVConfig.DebugSpawnKey.Value; if (!((KeyboardShortcut)(ref value)).IsDown() || Time.unscaledTime < _debugCooldownUntil) { return; } _debugCooldownUntil = Time.unscaledTime + 0.75f; if (!IsServer()) { HUDManager instance = HUDManager.Instance; if (instance != null) { instance.DisplayTip("FLYING TV", "Only the host can debug-spawn the flying TV.", true, false, "LC_Tip1"); } return; } int num = SpawnForAllLivingPlayers(); HUDManager instance2 = HUDManager.Instance; if (instance2 != null) { instance2.DisplayTip("FLYING TV", (num > 0) ? $"Spawned {num} flying TV event(s)." : "No living players available.", num <= 0, false, "LC_Tip1"); } } internal static int SpawnForAllLivingPlayers(string videoName = null, float durationSeconds = -1f) { if (!IsServer() || (Object)(object)Plugin.FlyingTVPrefab == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null) { return 0; } int num = 0; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (allPlayerScripts == null) { return 0; } for (int i = 0; i < allPlayerScripts.Length; i++) { if (IsValidLivingPlayer(allPlayerScripts[i]) && SpawnForPlayerIndex(i, videoName, durationSeconds)) { num++; } } return num; } internal static bool SpawnForPlayer(PlayerControllerB player, string videoName = null, float durationSeconds = -1f) { if ((Object)(object)player == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null) { return false; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (allPlayerScripts == null) { return false; } for (int i = 0; i < allPlayerScripts.Length; i++) { if ((Object)(object)allPlayerScripts[i] == (Object)(object)player) { return SpawnForPlayerIndex(i, videoName, durationSeconds); } } return false; } internal static bool SpawnForPlayerIndex(int targetPlayerIndex, string videoName = null, float durationSeconds = -1f) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) if (!IsServer() || (Object)(object)Plugin.FlyingTVPrefab == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null) { return false; } PlayerControllerB player = GetPlayer(targetPlayerIndex); if (!IsValidLivingPlayer(player)) { return false; } if (HasActiveTVForTarget(targetPlayerIndex)) { return false; } Vector3 val = BuildSpawnPosition(player); Quaternion val2 = BuildLookRotation(val, player); GameObject val3 = Object.Instantiate(Plugin.FlyingTVPrefab, val, val2); ((Object)val3).name = $"FlyingTV_Target{targetPlayerIndex}"; FlyingTVAi flyingTVAi = val3.GetComponent(); if ((Object)(object)flyingTVAi == (Object)null) { flyingTVAi = val3.AddComponent(); } NetworkObject component = val3.GetComponent(); if ((Object)(object)component == (Object)null) { Plugin.Log.LogError((object)"[FlyingTV] Cannot spawn: prefab instance has no NetworkObject."); Object.Destroy((Object)(object)val3); return false; } component.Spawn(true); flyingTVAi.InitializeServer(targetPlayerIndex, ResolveVideoName(videoName), ResolveDuration(durationSeconds)); Plugin.Log.LogInfo((object)$"[FlyingTV] Spawned for player index {targetPlayerIndex} at {val}."); return true; } internal static PlayerControllerB GetPlayer(int playerIndex) { PlayerControllerB[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.allPlayerScripts : null); if (array == null || playerIndex < 0 || playerIndex >= array.Length) { return null; } return array[playerIndex]; } internal static bool IsServer() { if ((Object)(object)NetworkManager.Singleton != (Object)null) { return NetworkManager.Singleton.IsServer; } return false; } internal static bool IsValidLivingPlayer(PlayerControllerB player) { if ((Object)(object)player != (Object)null && player.isPlayerControlled) { return !player.isPlayerDead; } return false; } private static bool HasActiveTVForTarget(int targetPlayerIndex) { FlyingTVAi[] array = Object.FindObjectsOfType(); foreach (FlyingTVAi flyingTVAi in array) { if ((Object)(object)flyingTVAi != (Object)null && ((NetworkBehaviour)flyingTVAi).IsSpawned && !((EnemyAI)flyingTVAi).isEnemyDead && ((EnemyAI)flyingTVAi).currentBehaviourStateIndex != 5 && (Object)(object)((EnemyAI)flyingTVAi).targetPlayer != (Object)null && GetPlayerIndex(((EnemyAI)flyingTVAi).targetPlayer) == targetPlayerIndex) { return true; } } return false; } private static int GetPlayerIndex(PlayerControllerB player) { if ((Object)(object)StartOfRound.Instance == (Object)null) { return -1; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (allPlayerScripts == null) { return -1; } for (int i = 0; i < allPlayerScripts.Length; i++) { if ((Object)(object)allPlayerScripts[i] == (Object)(object)player) { return i; } } return (int)player.playerClientId; } private static Vector3 BuildSpawnPosition(PlayerControllerB target) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) Vector3 val = FlatDirection(((Component)target).transform.forward, Vector3.forward); Vector3 val2 = FlatDirection(((Component)target).transform.right, Vector3.right); float num = ((Random.value < 0.5f) ? (-1f) : 1f); return ((Component)target).transform.position - val * 24f + val2 * 6f * num + Vector3.up * 7f; } internal static Quaternion BuildLookRotation(Vector3 fromPosition, PlayerControllerB target) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_0036: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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) Vector3 val = (((Object)(object)target != (Object)null) ? (((Component)target).transform.position + Vector3.up * 1.4f) : (fromPosition + Vector3.forward)) - fromPosition; if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.forward; } return BuildScreenFacingRotation(((Vector3)(ref val)).normalized); } internal static Vector3 FlatDirection(Vector3 direction, Vector3 fallback) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) direction.y = 0f; if (((Vector3)(ref direction)).sqrMagnitude < 0.001f) { direction = fallback; } direction.y = 0f; return ((Vector3)(ref direction)).normalized; } internal static Quaternion BuildScreenFacingRotation(Vector3 screenDirection) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_0032: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) screenDirection.y = 0f; if (((Vector3)(ref screenDirection)).sqrMagnitude < 0.001f) { screenDirection = Vector3.forward; } return Quaternion.LookRotation(((Vector3)(ref screenDirection)).normalized, Vector3.up) * Quaternion.Inverse(GetScreenLocalFacingRotation()); } internal static Vector3 GetScreenFacingDirection(Transform tvTransform) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_005b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)tvTransform == (Object)null) { return Vector3.forward; } Vector3 val = tvTransform.rotation * GetScreenLocalFacingDirection(); val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = tvTransform.forward; } val.y = 0f; if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return Vector3.forward; } return ((Vector3)(ref val)).normalized; } internal static Vector3 GetScreenLocalFacingDirection() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) return GetScreenLocalFacingRotation() * Vector3.forward; } private static Quaternion GetScreenLocalFacingRotation() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) return Quaternion.Euler(0f, 0f, 0f); } private static string ResolveVideoName(string requested) { return FlyingTVConfig.ResolveVideoSource(requested); } private static float ResolveDuration(float requested) { if (requested > 0f) { return requested; } if (FlyingTVConfig.PlaybackDurationSeconds == null) { return 0f; } return Mathf.Max(0f, FlyingTVConfig.PlaybackDurationSeconds.Value); } } internal sealed class FlyingTVSpawnWeights : IWeighted, IContextualWeighted { internal const int DefaultWeight = 6; internal const int MaximumWeight = 100; private readonly Func _multiplier; internal FlyingTVSpawnWeights(Func multiplier) { _multiplier = multiplier ?? throw new ArgumentNullException("multiplier"); } public int GetWeight() { return Scale(6, 1f); } public int GetWeight(SpawnWeightContext context) { int moonWeight = GetMoonWeight(((SpawnWeightContext)(ref context)).Moon); if (moonWeight <= 0) { return 0; } return Scale(moonWeight, GetDungeonMultiplier(((SpawnWeightContext)(ref context)).Dungeon)); } internal static int GetMoonWeight(DawnMoonInfo moon) { if (moon == null) { return 6; } if (((DawnBaseInfo)(object)moon).HasTag(Tags.Company) || ((DawnBaseInfo)(object)moon).HasTag(Tags.Unimplemented) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Test)) { return 0; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Experimentation)) { return 4; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Assurance) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Vow)) { return 6; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Offense) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.March) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Embrion)) { return 8; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Adamance)) { return 10; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Rend) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Dine)) { return 11; } if (KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Titan) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Artifice) || KeyEquals(((DawnBaseInfo)(object)moon).TypedKey, MoonKeys.Liquidation)) { return 13; } if (((DawnBaseInfo)(object)moon).HasTag(Tags.Paid)) { return 10; } ((DawnBaseInfo)(object)moon).HasTag(Tags.Free); return 6; } internal static float GetDungeonMultiplier(DawnDungeonInfo dungeon) { if (dungeon == null) { return 1f; } if (KeyEquals(((DawnBaseInfo)(object)dungeon).TypedKey, DungeonKeys.FacilityFlow) || KeyEquals(((DawnBaseInfo)(object)dungeon).TypedKey, DungeonKeys.FacilityFlowThreeExits) || KeyEquals(((DawnBaseInfo)(object)dungeon).TypedKey, DungeonKeys.FacilityFlowExtraLarge) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Industrial) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Mechanical) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Factory) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Bunker)) { return 1.25f; } if (KeyEquals(((DawnBaseInfo)(object)dungeon).TypedKey, DungeonKeys.MansionFlow) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Gothic) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Grand) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Ornate) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Lavish) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Eerie)) { return 0.75f; } if (KeyEquals(((DawnBaseInfo)(object)dungeon).TypedKey, DungeonKeys.MineshaftFlow) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Cavern) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Quarry) || ((DawnBaseInfo)(object)dungeon).HasTag(Tags.Flooded)) { return 0.5f; } return 0.75f; } private int Scale(int moonWeight, float dungeonMultiplier) { float num = Mathf.Clamp(_multiplier(), 0f, 5f); return Mathf.Clamp(Mathf.RoundToInt((float)moonWeight * dungeonMultiplier * num), 0, 100); } private static bool KeyEquals(NamespacedKey left, NamespacedKey right) where T : INamespaced { if (left != null && right != null) { return ((object)left).Equals((object?)right); } return false; } } internal static class FlyingTVTuning { internal const float FallbackDurationSeconds = 10f; internal const float MaximumUnknownDurationSeconds = 300f; internal const float SpawnDistance = 24f; internal const float SpawnHeight = 7f; internal const float SpawnSideOffset = 6f; internal const float FollowHeight = 2.4f; internal const float PlayerStopWindowSeconds = 1f; internal const float PlayerStopThreshold = 0.35f; internal const float LeaveSpeed = 7f; internal const float LeaveSeconds = 3f; internal const float ProximityAwarenessRange = 3f; internal const float ChaseLoseAggroDistance = 28f; internal const float ChaseLoseAggroSeconds = 4f; internal const float ApproachLoseAggroDistance = 45f; internal const float ApproachLoseAggroSeconds = 8f; internal const float HoverHeight = 1.9f; internal const float AgentAcceleration = 10f; internal const int RenderTextureWidth = 1024; internal const int RenderTextureHeight = 576; internal const float ScreenEmission = 2.2f; internal const float ScreenFacingYawOffsetDegrees = 0f; internal const float ScreenOverlayDepthOffset = 0.03f; internal const float ScreenPanelLocalX = 0f; internal const float ScreenPanelLocalY = -1.67f; internal const float ScreenPanelLocalDepth = 0.44f; internal const float ScreenPanelWidth = 1.06f; internal const float ScreenPanelHeight = 0.86f; } [BepInPlugin("y4ngz.lethalcompany.flyingtv", "Y4NGZ Flying TV", "0.1.17")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { internal const string BundleFileName = "flyingtv.bundle"; internal const string PrefabAssetName = "FlyingTVPrefab"; internal const string DawnNamespace = "y4ngz_monsters"; internal const string DawnEnemyKey = "warden"; internal const string ScanNodeSubtitle = "Stand still and face the screen"; internal const string BestiaryText = "Warden\n\nDanger level: low\n\nA floating television that approaches employees and demands they watch its documentation. Stand still and face the screen to remain safe. If you move or look away, it will become hostile.\n\n"; internal static ManualLogSource Log; private static bool _loggedScanNodeLayer; internal static AssetBundle Bundle; internal static GameObject FlyingTVPrefab; internal static EnemyType FlyingTVEnemyType; internal static string AssemblyDirectory; internal static AudioClip GreetingsClip; internal static AudioClip NoncomplianceClip; internal static AudioClip ComplianceClip; internal static AudioClip PropellerClip; private static bool _netcodeInitialized; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; try { InitializeNetcodeRPCs(); FlyingTVConfig.Bind(((BaseUnityPlugin)this).Config); AssemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (!FlyingTVConfig.Enabled.Value) { Log.LogInfo((object)"Y4NGZ Flying TV is disabled by config."); } else if (LoadBundle() && LoadAssets()) { LoadAudioClips(); RegisterEnemy(); FlyingTVManager.EnsureInstance(); Log.LogInfo((object)("Y4NGZ Flying TV v0.1.17 loaded from " + AssemblyDirectory + ". Flying TV enemy registered. Runtime marker: real-screen-face-marker + attack-noncompliance + vanilla-death-explosion + damage-wobble-search.")); } } catch (Exception arg) { Log.LogError((object)string.Format("Failed to initialize {0}: {1}", "Y4NGZ Flying TV", arg)); } } private static void InitializeNetcodeRPCs() { if (_netcodeInitialized) { return; } _netcodeInitialized = true; Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type type in types) { MethodInfo[] methods; try { methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch { continue; } foreach (MethodInfo methodInfo in methods) { if (methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false).Length == 0) { continue; } try { methodInfo.Invoke(null, null); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("[NetcodeInit] " + type.Name + "." + methodInfo.Name + " failed: " + ex.Message)); } } } } } private static bool LoadBundle() { if (string.IsNullOrEmpty(AssemblyDirectory)) { Log.LogError((object)"Could not resolve plugin assembly directory."); return false; } string text = Path.Combine(AssemblyDirectory, "flyingtv.bundle"); if (!File.Exists(text)) { Log.LogError((object)("Bundle not found at " + text + ". Build the Unity FlyingTV bundle and place it beside Y4NGZFlyingTV.dll.")); return false; } Bundle = AssetBundle.LoadFromFile(text); if ((Object)(object)Bundle == (Object)null) { Log.LogError((object)("AssetBundle.LoadFromFile returned null for " + text + ".")); return false; } Log.LogInfo((object)("Bundle loaded from " + text + ".")); return true; } private static bool LoadAssets() { FlyingTVPrefab = Plugin.LoadBundleAsset("FlyingTVPrefab", "/flyingtvprefab.prefab"); if ((Object)(object)FlyingTVPrefab == (Object)null) { Log.LogError((object)"Prefab 'FlyingTVPrefab' was not found in flyingtv.bundle."); return false; } EnsurePrefabRuntimeRefs(FlyingTVPrefab); return true; } private void LoadAudioClips() { GreetingsClip = LoadEmbeddedPcmWav("Greetings.wav"); NoncomplianceClip = LoadEmbeddedPcmWav("Noncompliance_01.wav"); ComplianceClip = LoadEmbeddedPcmWav("Compliance_01.wav"); PropellerClip = LoadEmbeddedPcmWav("PropellerLoop.wav"); Log.LogInfo((object)$"[FlyingTV] Audio: Greetings={(Object)(object)GreetingsClip != (Object)null}, Noncompliance={(Object)(object)NoncomplianceClip != (Object)null}, Compliance={(Object)(object)ComplianceClip != (Object)null}, Propeller={(Object)(object)PropellerClip != (Object)null}"); if ((Object)(object)FlyingTVPrefab != (Object)null) { ApplyAudioToPrefab(FlyingTVPrefab); } } private static AudioClip LoadEmbeddedPcmWav(string fileName) { try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = null; string value = "." + fileName; string[] manifestResourceNames = executingAssembly.GetManifestResourceNames(); for (int i = 0; i < manifestResourceNames.Length; i++) { if (manifestResourceNames[i].EndsWith(value, StringComparison.OrdinalIgnoreCase)) { text = manifestResourceNames[i]; break; } } if (text == null) { throw new FileNotFoundException("Embedded PCM WAV resource was not found.", fileName); } byte[] array; using (Stream stream = executingAssembly.GetManifestResourceStream(text)) { if (stream == null || stream.Length <= 0 || stream.Length > 16777216) { throw new InvalidDataException("Embedded PCM WAV resource has an invalid size."); } array = new byte[(int)stream.Length]; int num; for (int j = 0; j < array.Length; j += num) { num = stream.Read(array, j, array.Length - j); if (num <= 0) { throw new EndOfStreamException("Embedded PCM WAV resource ended unexpectedly."); } } } AudioClip val = DecodePcm16Wav(array, Path.GetFileNameWithoutExtension(fileName)); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"[FlyingTV] Loaded embedded audio {fileName}: {val.channels} channel(s), {val.frequency} Hz, {val.length:0.00}s."); } return val; } catch (Exception ex) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("[FlyingTV] Failed to load embedded audio " + fileName + ": " + ex.GetBaseException().Message)); } return null; } } private static AudioClip DecodePcm16Wav(byte[] bytes, string clipName) { if (bytes == null || bytes.Length < 44 || !MatchesFourCc(bytes, 0, "RIFF") || !MatchesFourCc(bytes, 8, "WAVE")) { throw new InvalidDataException("Audio resource is not a RIFF/WAVE file."); } int num = -1; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = -1; int num6 = 0; int num7 = 12; while (num7 + 8 <= bytes.Length) { int num8 = ReadInt32LittleEndian(bytes, num7 + 4); int num9 = num7 + 8; if (num8 < 0 || num9 > bytes.Length - num8) { throw new InvalidDataException("Audio resource contains a truncated WAV chunk."); } if (MatchesFourCc(bytes, num7, "fmt ")) { if (num8 < 16) { throw new InvalidDataException("Audio resource contains an invalid WAV format chunk."); } num = ReadUInt16LittleEndian(bytes, num9); num2 = ReadUInt16LittleEndian(bytes, num9 + 2); num3 = ReadInt32LittleEndian(bytes, num9 + 4); num4 = ReadUInt16LittleEndian(bytes, num9 + 14); } else if (MatchesFourCc(bytes, num7, "data")) { num5 = num9; num6 = num8; } num7 = num9 + num8 + (num8 & 1); } if (num != 1 || num4 != 16) { throw new InvalidDataException("Only uncompressed PCM16 WAV audio is supported."); } if (num2 < 1 || num2 > 8 || num3 < 8000 || num3 > 192000) { throw new InvalidDataException("Audio resource has invalid channel or sample-rate metadata."); } if (num5 < 0 || num6 <= 0 || num6 % (num2 * 2) != 0) { throw new InvalidDataException("Audio resource has an invalid PCM data chunk."); } int num10 = num6 / 2; int num11 = num10 / num2; float[] array = new float[num10]; for (int i = 0; i < num10; i++) { int num12 = num5 + i * 2; short num13 = (short)(bytes[num12] | (bytes[num12 + 1] << 8)); array[i] = (float)num13 / 32768f; } AudioClip val = AudioClip.Create(clipName, num11, num2, num3, false); if ((Object)(object)val == (Object)null || !val.SetData(array, 0)) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } throw new InvalidOperationException("Unity rejected the decoded PCM samples."); } return val; } private static bool MatchesFourCc(byte[] bytes, int offset, string expected) { if (offset >= 0 && expected != null && expected.Length == 4 && offset <= bytes.Length - 4 && bytes[offset] == (byte)expected[0] && bytes[offset + 1] == (byte)expected[1] && bytes[offset + 2] == (byte)expected[2]) { return bytes[offset + 3] == (byte)expected[3]; } return false; } private static int ReadUInt16LittleEndian(byte[] bytes, int offset) { if (offset < 0 || offset > bytes.Length - 2) { throw new InvalidDataException("Audio resource ended while reading WAV metadata."); } return bytes[offset] | (bytes[offset + 1] << 8); } private static int ReadInt32LittleEndian(byte[] bytes, int offset) { if (offset < 0 || offset > bytes.Length - 4) { throw new InvalidDataException("Audio resource ended while reading WAV metadata."); } return bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24); } private void ApplyAudioToPrefab(GameObject prefab) { FlyingTVAi component = prefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { if ((Object)(object)GreetingsClip != (Object)null) { component.greetingsClip = GreetingsClip; } if ((Object)(object)NoncomplianceClip != (Object)null) { component.noncomplianceClip = NoncomplianceClip; } if ((Object)(object)ComplianceClip != (Object)null) { component.complianceClip = ComplianceClip; } if ((Object)(object)PropellerClip != (Object)null) { component.propellerLoopClip = PropellerClip; } } } private void RegisterEnemy() { FlyingTVEnemyType = CreateEnemyType(FlyingTVPrefab); FlyingTVAi component = FlyingTVPrefab.GetComponent(); if ((Object)(object)component != (Object)null) { ((EnemyAI)component).enemyType = FlyingTVEnemyType; } FlyingTVSpawnWeights spawnWeights = new FlyingTVSpawnWeights(() => FlyingTVConfig.SpawnWeightMultiplier.Value); DawnLib.RegisterNetworkPrefab(FlyingTVPrefab); DawnLib.DefineEnemy(NamespacedKey.From("y4ngz_monsters", "warden"), FlyingTVEnemyType, (Action)delegate(EnemyInfoBuilder builder) { ((BaseInfoBuilder)(object)builder.DefineInside((Action)delegate(EnemyLocationBuilder location) { location.SetWeights((Action>)delegate(WeightTableBuilder table) { table.SetGlobalWeight((IWeighted)(object)spawnWeights); }); }).CreateBestiaryNode("Warden\n\nDanger level: low\n\nA floating television that approaches employees and demands they watch its documentation. Stand still and face the screen to remain safe. If you move or look away, it will become hostile.\n\n").CreateNameKeyword("warden")).AddTags((IEnumerable)(object)new NamespacedKey[4] { Tags.Hostile, Tags.Mechanical, Tags.Killable, Tags.Medium }); }); Log.LogInfo((object)"[FlyingTV] Enemy registered as 'Warden'."); } private static EnemyType CreateEnemyType(GameObject prefab) { EnemyType obj = ScriptableObject.CreateInstance(); obj.enemyName = "Warden"; obj.enemyPrefab = prefab; obj.PowerLevel = FlyingTVConfig.PowerLevel.Value; obj.DiversityPowerLevel = 1; obj.MaxCount = FlyingTVConfig.MaxCount.Value; obj.canDie = true; obj.canBeStunned = true; obj.canBeDestroyed = true; obj.stunTimeMultiplier = 1f; obj.stunGameDifficultyMultiplier = 1f; obj.destroyOnDeath = false; obj.doorSpeedMultiplier = 1f; obj.isOutsideEnemy = false; obj.isDaytimeEnemy = false; obj.normalizedTimeInDayToLeave = 1f; obj.probabilityCurve = AnimationCurve.Constant(0f, 1f, 1f); obj.numberSpawnedFalloff = AnimationCurve.Linear(0f, 1f, 1f, 0.55f); obj.useNumberSpawnedFalloff = true; obj.disableAnimatorWhenFar = false; return obj; } internal static void EnsurePrefabRuntimeRefs(GameObject prefab) { TrySetTag(prefab, "Enemy"); NetworkObject val = prefab.GetComponent(); if ((Object)(object)val == (Object)null) { val = prefab.AddComponent(); } val.SynchronizeTransform = true; if ((Object)(object)prefab.GetComponent() == (Object)null) { prefab.AddComponent(); } NavMeshAgent val2 = prefab.GetComponentInChildren(true); if ((Object)(object)val2 == (Object)null) { val2 = prefab.AddComponent(); } val2.updatePosition = true; val2.updateRotation = false; val2.updateUpAxis = false; val2.baseOffset = 0f; val2.radius = 0.65f; val2.height = 1.35f; val2.autoTraverseOffMeshLink = true; if (val2.areaMask == 0) { val2.areaMask = -1; } ((Behaviour)val2).enabled = true; FlyingTVAi flyingTVAi = prefab.GetComponent(); if ((Object)(object)flyingTVAi == (Object)null) { flyingTVAi = prefab.AddComponent(); } EnsureScanNode(prefab); EnsureCollision(prefab, flyingTVAi); HideHelperVisuals(prefab.transform); ((EnemyAI)flyingTVAi).creatureAnimator = prefab.GetComponentInChildren(true); if ((Object)(object)((EnemyAI)flyingTVAi).creatureAnimator != (Object)null) { ((EnemyAI)flyingTVAi).creatureAnimator.applyRootMotion = false; ((EnemyAI)flyingTVAi).creatureAnimator.cullingMode = (AnimatorCullingMode)0; } if ((Object)(object)flyingTVAi.visualRoot == (Object)null) { Transform val3 = prefab.transform.Find("VisualRoot"); if ((Object)(object)val3 != (Object)null) { flyingTVAi.visualRoot = val3; } } if ((Object)(object)flyingTVAi.screenRenderer == (Object)null) { Transform val4 = FindChildRecursive(prefab.transform, "TV.smd") ?? FindChildRecursive(prefab.transform, "TV") ?? FindChildRecursive(prefab.transform, "FlyingTVEmbeddedScreenSurface") ?? FindChildRecursive(prefab.transform, "FlyingTVScreenSurface"); if ((Object)(object)val4 != (Object)null) { flyingTVAi.screenRenderer = ((Component)val4).GetComponent(); } } if ((Object)(object)flyingTVAi.videoAudioSource == (Object)null) { Transform val5 = FindChildRecursive(prefab.transform, "VideoAudio"); if ((Object)(object)val5 != (Object)null) { flyingTVAi.videoAudioSource = ((Component)val5).GetComponent(); } } AudioSource[] componentsInChildren = prefab.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { DisableUnsupportedSpatialization(componentsInChildren[i]); } } private static void DisableUnsupportedSpatialization(AudioSource source) { if (!((Object)(object)source == (Object)null)) { source.spatialize = false; source.spatializePostEffects = false; } } internal static void EnsureScanNode(GameObject prefab) { //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) ScanNodeProperties val = null; ScanNodeProperties[] componentsInChildren = prefab.GetComponentsInChildren(true); foreach (ScanNodeProperties val2 in componentsInChildren) { bool flag = val2.creatureScanID >= 0; bool flag2 = (Object)(object)val != (Object)null && val.creatureScanID >= 0; bool flag3 = string.Equals(((Object)((Component)val2).gameObject).name, "ScanNode", StringComparison.Ordinal); bool flag4 = (Object)(object)val != (Object)null && string.Equals(((Object)((Component)val).gameObject).name, "ScanNode", StringComparison.Ordinal); if ((Object)(object)val == (Object)null || (flag && !flag2) || (flag == flag2 && flag3 && !flag4)) { val = val2; } } if (componentsInChildren.Length > 1) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"[FlyingTV] Found {componentsInChildren.Length} scan nodes; retiring every duplicate."); } } for (int j = 0; j < componentsInChildren.Length; j++) { if ((Object)(object)componentsInChildren[j] != (Object)(object)val) { RetireExtraScanNode(componentsInChildren[j], val, prefab); } } Transform val3 = (((Object)(object)val != (Object)null) ? ((Component)val).transform : FindOrCreateChild(prefab.transform, "ScanNode", new Vector3(0f, 0.25f, 0f))); ((Object)((Component)val3).gameObject).name = "ScanNode"; TrySetTag(((Component)val3).gameObject, "DoNotSet"); TrySetLayer(((Component)val3).gameObject, "ScanNode"); EnsureHierarchyActive(val3, prefab.transform); ((Component)val3).gameObject.SetActive(true); if (!_loggedScanNodeLayer) { _loggedScanNodeLayer = true; ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)($"[FlyingTV] Scan node layer resolved to {((Component)val3).gameObject.layer} " + string.Format("(NameToLayer(\"ScanNode\")={0}, fallback 22); ", LayerMask.NameToLayer("ScanNode")) + $"active={((Component)val3).gameObject.activeInHierarchy}.")); } } BoxCollider val4 = ((Component)val3).GetComponent(); if ((Object)(object)val4 == (Object)null) { val4 = ((Component)val3).gameObject.AddComponent(); } ((Collider)val4).enabled = true; ((Collider)val4).isTrigger = true; val4.center = Vector3.zero; val4.size = new Vector3(1.4f, 1.2f, 0.75f); Rigidbody obj = ((Component)val3).GetComponent() ?? ((Component)val3).gameObject.AddComponent(); obj.isKinematic = true; obj.useGravity = false; obj.detectCollisions = true; if ((Object)(object)val == (Object)null) { val = ((Component)val3).gameObject.AddComponent(); } val.headerText = "Warden"; val.subText = "Stand still and face the screen"; val.maxRange = 20; val.minRange = 1; val.requiresLineOfSight = true; val.nodeType = 1; } private static void RetireExtraScanNode(ScanNodeProperties extra, ScanNodeProperties preferred, GameObject root) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)extra == (Object)null) { return; } Scene scene; if ((Object)(object)((Component)extra).gameObject == (Object)(object)root || ((Object)(object)preferred != (Object)null && (Object)(object)((Component)extra).gameObject == (Object)(object)((Component)preferred).gameObject)) { scene = root.scene; if (((Scene)(ref scene)).IsValid()) { Object.Destroy((Object)(object)extra); } else { Object.DestroyImmediate((Object)(object)extra, true); } return; } GameObject gameObject = ((Component)extra).gameObject; gameObject.SetActive(false); Collider[] components = gameObject.GetComponents(); for (int i = 0; i < components.Length; i++) { components[i].enabled = false; } scene = root.scene; if (((Scene)(ref scene)).IsValid()) { Object.Destroy((Object)(object)gameObject); } else { Object.DestroyImmediate((Object)(object)gameObject, true); } } private static void EnsureCollision(GameObject prefab, FlyingTVAi ai) { //IL_002a: 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_00d5: Unknown result type (might be due to invalid IL or missing references) TrySetLayer(prefab, "Enemies"); CapsuleCollider obj = prefab.GetComponent() ?? prefab.AddComponent(); ((Collider)obj).isTrigger = false; obj.direction = 1; obj.center = Vector3.zero; obj.radius = 0.65f; obj.height = 1.35f; Rigidbody obj2 = prefab.GetComponent() ?? prefab.AddComponent(); obj2.isKinematic = true; obj2.useGravity = false; obj2.detectCollisions = true; obj2.collisionDetectionMode = (CollisionDetectionMode)3; Transform val = FindOrCreateChild(prefab.transform, "Collision", Vector3.zero); TrySetTag(((Component)val).gameObject, "Enemy"); TrySetLayer(((Component)val).gameObject, "Enemies"); CapsuleCollider obj3 = ((Component)val).GetComponent() ?? ((Component)val).gameObject.AddComponent(); ((Collider)obj3).enabled = true; ((Collider)obj3).isTrigger = true; obj3.direction = 1; obj3.center = Vector3.zero; obj3.radius = 0.65f; obj3.height = 1.35f; Rigidbody obj4 = ((Component)val).GetComponent() ?? ((Component)val).gameObject.AddComponent(); obj4.isKinematic = true; obj4.useGravity = false; obj4.detectCollisions = true; EnemyAICollisionDetect obj5 = ((Component)val).GetComponent() ?? ((Component)val).gameObject.AddComponent(); obj5.mainScript = (EnemyAI)(object)ai; obj5.alwaysAllowHitting = true; obj5.canCollideWithEnemies = true; obj5.onlyCollideWhenGrounded = false; DisableRenderers(val); } private static void HideHelperVisuals(Transform root) { if ((Object)(object)root == (Object)null) { return; } for (int i = 0; i < root.childCount; i++) { Transform child = root.GetChild(i); if (IsHelperVisualName(((Object)child).name)) { DisableRenderers(child); } else { HideHelperVisuals(child); } } } private static bool IsHelperVisualName(string name) { if (string.IsNullOrWhiteSpace(name)) { return false; } string text = name.ToLowerInvariant(); if (!(text == "icosphere") && !text.Contains("collision") && !text.Contains("collider") && !text.Contains("hitbox") && !text.Contains("hurtbox") && !text.Contains("scan") && !text.Contains("trigger")) { return text.Contains("audio"); } return true; } private static void DisableRenderers(Transform root) { Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = false; } } private static Transform FindOrCreateChild(Transform parent, string name, Vector3 localPosition) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) Transform val = parent.Find(name); if ((Object)(object)val != (Object)null) { return val; } val = new GameObject(name).transform; val.SetParent(parent, false); val.localPosition = localPosition; return val; } private static void TrySetTag(GameObject obj, string tag) { try { obj.tag = tag; } catch { } } private static void TrySetLayer(GameObject obj, string layerName) { if ((Object)(object)obj == (Object)null) { return; } int num = LayerMask.NameToLayer(layerName); if (num < 0) { num = ResolveLayerFallback(layerName); } if (num >= 0 && num <= 31) { obj.layer = num; return; } ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"[FlyingTV] Layer '{layerName}' unresolved; '{((Object)obj).name}' stays on layer {obj.layer}."); } } private static int ResolveLayerFallback(string layerName) { if (string.Equals(layerName, "ScanNode", StringComparison.OrdinalIgnoreCase)) { return 22; } if (string.Equals(layerName, "Enemies", StringComparison.OrdinalIgnoreCase)) { return 19; } return -1; } private static void EnsureHierarchyActive(Transform child, Transform stopAt) { Transform val = child; while ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(true); if (!((Object)(object)val == (Object)(object)stopAt)) { val = val.parent; continue; } break; } } internal static T LoadBundleAsset(string shortName, string pathEnding) where T : Object { T val = Bundle.LoadAsset(shortName); if ((Object)(object)val != (Object)null) { return val; } string[] allAssetNames = Bundle.GetAllAssetNames(); foreach (string text in allAssetNames) { string text2 = text.Replace('\\', '/'); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text2); if (text2.EndsWith(pathEnding, StringComparison.OrdinalIgnoreCase) || string.Equals(fileNameWithoutExtension, shortName, StringComparison.OrdinalIgnoreCase)) { val = Bundle.LoadAsset(text); if ((Object)(object)val != (Object)null) { return val; } } } return default(T); } internal static Transform FindChildRecursive(Transform root, string childName) { if ((Object)(object)root == (Object)null || string.IsNullOrWhiteSpace(childName)) { return null; } if (string.Equals(((Object)root).name, childName, StringComparison.OrdinalIgnoreCase)) { return root; } for (int i = 0; i < root.childCount; i++) { Transform val = FindChildRecursive(root.GetChild(i), childName); if ((Object)(object)val != (Object)null) { return val; } } return null; } } internal static class PluginInfo { public const string PLUGIN_GUID = "y4ngz.lethalcompany.flyingtv"; public const string PLUGIN_NAME = "Y4NGZ Flying TV"; public const string PLUGIN_VERSION = "0.1.17"; } 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 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 = Plugin.Log; if (log != null) { log.LogWarning((object)("[FlyingTV] 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 = Plugin.Log; if (log != null) { log.LogInfo((object)("[FlyingTV] 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 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[FlyingTV] Downloading and caching YouTube video " + GetSafeSourceLabel(normalizedUrl) + ".")); } try { ProcessResult processResult = await RunYtDlpAsync(binaryPath, ffmpegPath, workingVideoPath, normalizedUrl).ConfigureAwait(continueOnCapturedContext: false); if (processResult.TimedOut) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[FlyingTV] yt-dlp timed out while caching the configured video."); } return null; } if (processResult.ExitCode != 0) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)$"[FlyingTV] yt-dlp exited with code {processResult.ExitCode}: {GetSafeError(processResult.StandardError)}"); } return null; } if (!IsValidCachedVideo(workingVideoPath)) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogWarning((object)"[FlyingTV] 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 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)($"[FlyingTV] 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 = Plugin.Log; if (log7 != null) { log7.LogWarning((object)"[FlyingTV] FFmpeg timed out while removing encoded video borders; using the original frame."); } } else if (processResult2.ExitCode != 0 || !IsValidCachedVideo(normalizedVideoPath)) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogWarning((object)("[FlyingTV] FFmpeg could not remove encoded video borders; using the original frame: " + GetSafeError(processResult2.StandardError))); } } else { completedVideoPath = normalizedVideoPath; } } File.Move(completedVideoPath, cachedVideoPath); ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogInfo((object)("[FlyingTV] 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) ? Plugin.AssemblyDirectory : Paths.CachePath) ?? string.Empty, "Y4NGZFlyingTV"); 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 = Plugin.Log; if (log != null) { log.LogInfo((object)(exists ? "[FlyingTV] Refreshing the cached yt-dlp executable." : "[FlyingTV] 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 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[FlyingTV] 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 = Plugin.Log; if (log != null) { log.LogInfo((object)"[FlyingTV] 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 = Plugin.Log; if (log != null) { log.LogWarning((object)"[FlyingTV] FFmpeg timed out while checking the video frame for encoded borders."); } return null; } if (processResult.ExitCode != 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[FlyingTV] 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("Y4NGZFlyingTV/0.1.17"); 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 Y4NGZMonsters.Shared { internal static class ConfigSectionNameMigration { private static readonly IReadOnlyDictionary SectionNames = new Dictionary(StringComparer.Ordinal) { { "00 - General", "General" }, { "10 - Spawning", "Spawning" }, { "20 - Core Stats", "Core Stats" }, { "20 - Video", "Video" }, { "30 - Detection", "Detection" }, { "30 - Encounter Pacing", "Encounter Pacing" }, { "30 - Encounter Rules", "Encounter Rules" }, { "40 - Movement", "Movement" }, { "50 - Combat", "Combat" }, { "60 - Abilities", "Abilities" }, { "60 - Presentation", "Presentation" }, { "60 - Audio", "Audio" }, { "70 - Audio and Presentation", "Audio and Presentation" }, { "99 - Diagnostics", "Diagnostics" } }; private static readonly Regex NumberedSectionHeader = new Regex("^(?[ \\t]*)\\[(?
00 - General|10 - Spawning|20 - Core Stats|20 - Video|30 - Detection|30 - Encounter Pacing|30 - Encounter Rules|40 - Movement|50 - Combat|60 - Abilities|60 - Presentation|60 - Audio|70 - Audio and Presentation|99 - Diagnostics)\\](?[ \\t]*)(?\\r?)$", RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant); internal static bool TryMigrate(ConfigFile config, ManualLogSource log, string creatureName) { string configFilePath = config.ConfigFilePath; if (!File.Exists(configFilePath)) { return false; } string text = null; try { int replacementCount; string contents = RenameSectionHeaders(File.ReadAllText(configFilePath), out replacementCount); if (replacementCount == 0) { return false; } text = GetBackupPath(configFilePath); File.Copy(configFilePath, text, overwrite: false); File.WriteAllText(configFilePath, contents, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); config.Reload(); if (log != null) { log.LogInfo((object)($"[{creatureName}] Removed numeric prefixes from {replacementCount} config section header(s). " + "The previous config is backed up at '" + text + "'.")); } return true; } catch (Exception ex) { string text2 = "The original config was not modified."; if (!string.IsNullOrEmpty(text) && File.Exists(text)) { try { File.Copy(text, configFilePath, overwrite: true); config.Reload(); text2 = "The original config was restored from its backup."; } catch (Exception ex2) { text2 = "Automatic restore also failed (" + ex2.Message + "). Restore '" + text + "' manually before editing the config."; } } if (log != null) { log.LogWarning((object)("[" + creatureName + "] Could not remove numeric config section prefixes: " + ex.Message + " " + text2)); } return false; } } internal static string RenameSectionHeaders(string text, out int replacementCount) { int count = 0; string result = NumberedSectionHeader.Replace(text, delegate(Match match) { string value = match.Groups["section"].Value; if (!SectionNames.TryGetValue(value, out var value2)) { return match.Value; } count++; return match.Groups["leading"].Value + "[" + value2 + "]" + match.Groups["trailing"].Value + match.Groups["carriage"].Value; }); replacementCount = count; return result; } private static string GetBackupPath(string configPath) { string text = configPath + ".pre-section-names.bak"; int num = 1; while (File.Exists(text)) { text = configPath + $".pre-section-names.{num}.bak"; num++; } return text; } } } namespace __GEN { internal class NetworkVariableSerializationHelper { [RuntimeInitializeOnLoadMethod] internal static void InitializeSerialization() { } } } namespace Y4NGZFlyingTV.NetcodePatcher { [AttributeUsage(AttributeTargets.Module)] internal class NetcodePatchedAssemblyAttribute : Attribute { } }