using System.IO; using ADOFAIRenderer.Core; using ADOFAIRenderer.Util; using HarmonyLib; using UnityEngine; namespace ADOFAIRenderer.Patch.Game; [HarmonyPatch(typeof(scnGame))] // ReSharper disable once InconsistentNaming public static class scnGamePatch { [HarmonyPostfix] [HarmonyPatch("Play")] public static void ProcessFloors() { var nonTile = RenderManager.Instance.stage >= RenderStage.Load && RenderManager.Instance.Configuration.AdvancedConfig.NonTile; if (!nonTile) return; foreach (var floor in scrLevelMaker.instance.listFloors) { DisableRenderers( floor.floorRenderer.renderer, floor.legacyFloorSpriteRenderer, floor.iconsprite, floor.outlineSprite, floor.topGlow, floor.bottomGlow, floor.multiplanetLine ); if (floor.holdRenderer) DisableRenderer(floor.holdRenderer.m_meshRenderer); if (floor.dummyPlanets == null) continue; foreach (var planet in floor.dummyPlanets) DisablePlanetRenderer(planet); } var players = scrPlayerManager.instance.players; if (players == null) return; foreach (var player in players) { var planets = player.planetarySystem?.planetList; if (planets == null) continue; foreach (var planet in planets) DisablePlanetRenderer(planet.planetRenderer); } return; void DisablePlanetRenderer(PlanetRenderer planet) { if (planet.sprite) DisableRenderer(planet.sprite.meshRenderer); DisableRenderers( planet.coreParticles.GetRenderer(), planet.tailParticles.GetRenderer(), planet.tailParticlesCoop.GetRenderer(), planet.sparks.GetRenderer(), planet.ring, planet.glow, planet.faceSprite, planet.faceDetails, planet.samuraiSprite ); } void DisableRenderers(params Renderer?[] renderers) { foreach (var renderer in renderers) DisableRenderer(renderer); } void DisableRenderer(Renderer? renderer) { if (renderer) renderer.enabled = false; } } [HarmonyPrefix] [HarmonyPatch("UpdateVideo")] // ReSharper disable once InconsistentNaming public static bool ReplaceVideo(scnGame __instance) { // Only replace when rendering is active if (!RenderManager.IsActive) { // Remove FFmpeg player if it was attached previously var oldPlayer = __instance.videoBG.gameObject.GetComponent(); if (oldPlayer) Object.Destroy(oldPlayer); // Re-enable native player component __instance.videoBG.enabled = true; return true; // Execute original } string text = __instance.levelData.miscSettings.GetString("bgVideo"); if (string.IsNullOrEmpty(text)) { __instance.videoBG.gameObject.SetActive(false); return false; // Skip original } __instance.videoBG.gameObject.SetActive(true); // We can only play external video files via FFmpegNativeDecoder. // Internal/Bundle level background videos are built-in VideoClips, so we fall back to native Unity VideoPlayer. if (ADOBase.isInternalLevel || ADOBase.isBundleLevel) { __instance.videoBG.enabled = true; return true; // Execute original } // Custom level video path var videoPath = Path.Combine(Path.GetDirectoryName(ADOBase.levelPath) ?? "", text); if (!File.Exists(videoPath)) { return true; // Let the original print error } // Disable native VideoPlayer component (to prevent it from attempting real-time playback and sync errors) var targetCamera = __instance.videoBG.targetCamera; if (!targetCamera) { var camGo = GameObject.Find("BGStaticCam"); if (camGo != null) { targetCamera = camGo.GetComponent(); } } __instance.videoBG.enabled = false; // Add or retrieve our custom FFmpeg video background player if (!__instance.videoBG.gameObject.TryGetComponent(out var player)) { player = __instance.videoBG.gameObject.AddComponent(); } var isLooping = __instance.levelData.miscSettings.GetBool("loopVideo"); // Initialize player. If targetTexture is null (CameraFarPlane mode), it will build a quad in front of targetCamera. player.Init(videoPath, __instance.videoBG.targetTexture, isLooping, targetCamera, __instance.videoBG.gameObject); return false; // Skip original UpdateVideo } }