#if UNITY_EDITOR using System; using System.Collections; using BlockOutReplica.Infrastructure; using UnityEngine; namespace BlockOutReplica.Gameplay { public sealed partial class BlockOutGameController { private BlockOutProgress savedLoopProgress; private BlockOutPlayerState savedLoopPlayer; private BlockOutJourney savedLoopJourney; private BlockOutEconomy savedLoopEconomy; private bool savedLoopHome; private bool savedLoopJourneyTab; private string savedLoopJourneyNotice; private int savedLoopCelebrationLevel; private float savedLoopCelebrationTime; public bool IsManualLoopSandbox { get; private set; } public bool IsFullLoopSandbox => savedLoopProgress!=null; public void BeginFullLoopSandbox(bool manual=false) { if(IsFullLoopSandbox) return; savedLoopProgress=progress;savedLoopPlayer=Player;savedLoopJourney=Journey;savedLoopEconomy=economy; savedLoopHome=ScreenState==BlockOutScreenState.Home; savedLoopJourneyTab=JourneyTab;savedLoopJourneyNotice=JourneyNotice; savedLoopCelebrationLevel=JourneyCelebrationLevel;savedLoopCelebrationTime=JourneyCelebrationStartedAt; IsManualLoopSandbox=manual; economy=BlockOutEconomy.Load(); economy.InitialUfoAmount=30;economy.InitialRocketAmount=30; player=new BlockOutPlayerState(economy,"",_=>{}); journey=new BlockOutJourney(BlockOutJourneyConfig.Load(),"",_=>{}); // Controlled Home fixture at 9 exercises the exact win-9 -> current10 // native boundary; it does not claim original early Home accessibility. progress=new BlockOutProgress(OriginalFlowRepository.Load(),false,9); InitializeInteractions();LoadCurrentLevel();GoHome(); Debug.Log("BLOCKOUT_FULL_LOOP_SANDBOX: started, level=9, isolatedSaves=True"); } public void RestoreFullLoopSandbox() { if(!IsFullLoopSandbox)return; progress=savedLoopProgress;player=savedLoopPlayer;journey=savedLoopJourney;economy=savedLoopEconomy; savedLoopProgress=null;savedLoopPlayer=null;savedLoopJourney=null;savedLoopEconomy=null; IsManualLoopSandbox=false; IsFirstTwentyPreview=false; InitializeInteractions();LoadCurrentLevel();if(savedLoopHome)GoHome(false); JourneyTab=savedLoopJourneyTab;JourneyNotice=savedLoopJourneyNotice; JourneyCelebrationLevel=savedLoopCelebrationLevel;JourneyCelebrationStartedAt=savedLoopCelebrationTime; JourneyRevision++; Debug.Log("BLOCKOUT_FULL_LOOP_SANDBOX: restored, persistentPlayerDataUntouched=True"); } public IEnumerator ReviewFullLoop() { if(IsFullLoopSandbox)throw new InvalidOperationException("Restore manual sandbox before running automated review."); int checks=0; Action check=(ok,name)=>{if(!ok)throw new InvalidOperationException(name);checks++;}; BeginFullLoopSandbox(); try { check(ScreenState==BlockOutScreenState.Home&&LevelNumber==9,"controlled Home fixture at level 9"); RequestEnterLevel(); check(Modal==GameModal.PreLevel,"pre-level"); EnterPreparedLevel();IsInputReviewRunning=true;yield return null; check(pieces.Count>0&&LevelNumber==9,"real level 9 loaded"); ClickBooster(BoosterType.Freeze);check(Player.Amount(BoosterType.Freeze)==2&&FreezeRemaining==10,"freeze activated"); yield return WaitReviewCondition(()=>FreezeAnimationTime>=.42f,"freeze pose"); CaptureLoop("freeze");yield return null; yield return WaitReviewCondition(()=>FreezeAnimationTime>=2.3f,"ice clock pose"); check(HasNativeFreezeScreen&&ActiveFreezeParticleCount>0,"native freeze screen and ice particles visible"); CaptureLoop("freeze-clock");yield return null; RequestPause();float frozenAnimation=FreezeAnimationTime;int frozenParticles=ActiveFreezeParticleCount; yield return WaitReviewTime(.25f); check(FreezeAnimationTime==frozenAnimation&&ActiveFreezeParticleCount==frozenParticles,"native freeze particles pause");CloseModal(); yield return WaitReviewCondition(()=>FreezeRemaining<=0&&!HasNativeFreezeScreen,"freeze end cleanup"); check(ActiveFreezeParticleCount==0,"ice effect expires and cleans up"); ClickBooster(BoosterType.Rocket); var target=pieces[0];BeginDrag(gameCamera.WorldToScreenPoint(target.transform.position),gameCamera); yield return WaitReviewCondition(()=>BoosterPlaybackTime>=.2f,"rocket entry pose");CaptureLoop("rocket-entry");yield return null; RequestPause();float time=TimeLeft;yield return WaitReviewTime(.25f); check(TimeLeft==time&&InputLocked,"effect and timer paused");CloseModal(); yield return WaitReviewCondition(()=>BoosterPlaybackTime>=1.45f,"rocket impact pose"); check(ActiveBoosterParticleCount>0,"native rocket impact particles emitted");CaptureLoop("rocket-impact");yield return null; while(InputLocked&&ScreenState==BlockOutScreenState.Playing)yield return null; check(target==null&&activeExits==0,"rocket impact retired target"); int guard=0; while(ScreenState==BlockOutScreenState.Playing&&pieces.Count>0&&guard++<12) { ClickBooster(BoosterType.Ufo);target=pieces[0]; BeginDrag(gameCamera.WorldToScreenPoint(target.transform.position),gameCamera); yield return WaitReviewCondition(()=>BoosterPlaybackTime>=1.55f,"ufo absorption pose");CaptureLoop("ufo-absorb");yield return null; yield return WaitReviewCondition(()=>BoosterPlaybackTime>=1.85f,"ufo beam pose"); check(ActiveBoosterParticleCount>0,"native UFO beam particles emitted");CaptureLoop("ufo-beam");yield return null; while(InputLocked&&ScreenState==BlockOutScreenState.Playing)yield return null; } check(ScreenState==BlockOutScreenState.LevelComplete,"actual board clear, no debug win"); check(LevelNumber==10&&Player.Coins==1020&&Journey.IsQueued(10)&&!Journey.IsClaimed(10),"win 9 pays ordinary20 and queues Journey50"); CaptureLoop("win");yield return null;ContinueAfterWin(); check(ScreenState==BlockOutScreenState.Home&&LevelNumber==10,"returned Home at current level 10"); check(Journey.IsClaimed(10)&&!Journey.IsQueued(10)&&Player.Coins==1070,"source level 10 reward automatically received"); ClaimJourneyReward(10);ClaimJourneyReward(10); check(Player.Coins==1070&&Journey.IsClaimed(10),"repeated settlement cannot duplicate 50 coins"); OpenHomeTab(true); CaptureLoop("journey-claimed");yield return null; OpenHomeTab(false);CaptureLoop("home");yield return null; RequestEnterLevel();EnterPreparedLevel();IsInputReviewRunning=true;yield return null; check(ScreenState==BlockOutScreenState.Playing&&LevelNumber==10,"current level 10 enters from Home"); Debug.Log($"BLOCKOUT_FULL_LOOP_REVIEW: passed=True, checks={checks}, realBoardClear=True, debugWin=False, persistentPlayerDataUntouched=True"); } finally{RestoreFullLoopSandbox();} } private IEnumerator WaitReviewTime(float seconds) {float end=Time.realtimeSinceStartup+seconds;while(Time.realtimeSinceStartup condition,string name) { float end=Time.realtimeSinceStartup+45; while(!condition()) {if(Time.realtimeSinceStartup>end)throw new InvalidOperationException("Timed out: "+name);yield return null;} } private static void CaptureLoop(string suffix) {System.IO.Directory.CreateDirectory("Docs/iterations");ScreenCapture.CaptureScreenshot("Docs/iterations/2026-10-19-full-loop-"+suffix+".png");} } } #endif