GameFlow.cs

The GameFlow static class manages the entire application state, screen transitions, and overall game lifecycle. It handles inputs, updates internal state based on screen changes, and triggers initialization routines for various game modes like studio editing and playtesting.

File Access
namespace PixelPusher;

public enum GameScreen
{
	Title,
	ModePick,
	Pixel,
	Stage,
	World,
	Chip,
	Library,
	Play2D,
	Play3D,
	Pause,
	Cleared
}

public static class GameFlow
{
	public static GameScreen Screen { get; private set; } = GameScreen.Title;
	public static GameScreen ResumeTo { get; private set; } = GameScreen.Pixel;
	public static bool PlaytestFromStage { get; private set; }
	public static GameScreen AfterPlay { get; private set; } = GameScreen.ModePick;
	public static int Ui { get; private set; }
	public static string Toast { get; private set; } = "";
	static float _toastLeft;

	public static bool InStudio =>
		Screen is GameScreen.Pixel or GameScreen.Stage or GameScreen.World or GameScreen.Chip or GameScreen.Library;

	public static bool InPlay => Screen is GameScreen.Play2D or GameScreen.Play3D;

	public static void Bump() => Ui++;

	public static void Tick()
	{
		if ( _toastLeft > 0f )
		{
			_toastLeft -= Time.Delta;
			if ( _toastLeft <= 0f )
			{
				Toast = "";
				Bump();
			}
		}

		if ( Screen == GameScreen.Title )
			return;

		if ( Input.Pressed( "Pause" ) || Input.Pressed( "Menu" ) )
		{
			TogglePause();
			return;
		}

		if ( InStudio )
		{
			if ( Input.Pressed( "Slot1" ) ) OpenStudio( GameScreen.Pixel );
			else if ( Input.Pressed( "Slot2" ) ) OpenStudio( GameScreen.Stage );
			else if ( Input.Pressed( "Slot3" ) ) OpenStudio( GameScreen.World );
			else if ( Input.Pressed( "Slot4" ) ) OpenStudio( GameScreen.Chip );
			else if ( Input.Pressed( "Slot5" ) ) OpenStudio( GameScreen.Library );

			if ( Screen == GameScreen.Stage && (Input.Pressed( "Score" ) || MoveKeys.DropIn) )
				StartPlaytest();
		}
	}

	public static void ShowToast( string text, float seconds = 2.2f )
	{
		Toast = text ?? "";
		_toastLeft = seconds;
		ChipVoices.Tick();
		Bump();
	}

	public static void GoTitle()
	{
		Desk.Autosave( quiet: true );
		PlaytestFromStage = false;
		AfterPlay = GameScreen.ModePick;
		Screen = GameScreen.Title;
		Bump();
	}

	public static void OpenModePick()
	{
		if ( !Desk.Ready )
			Desk.Boot();
		Screen = GameScreen.ModePick;
		Bump();
	}

	public static void EnterBuild()
	{
		if ( !Desk.Ready )
			Desk.Boot();
		Lesson.MaybeStart();
		Coach.OnEnterBuild();
		OpenStudio( GameScreen.Pixel );
	}

	public static void StartTour()
	{
		if ( !Desk.Ready )
			Desk.Boot();
		Lesson.Start();
		Coach.OnEnterBuild();
		OpenStudio( GameScreen.Pixel );
	}

	public static void EnterPlay()
	{
		if ( !Desk.Ready )
			Desk.Boot();
		PlaytestFromStage = false;
		AfterPlay = GameScreen.ModePick;
		PlaySim.Start( Desk.Stage, fromPlaytest: false );
		Coach.OnPlay();
		Screen = GameScreen.Play2D;
		Bump();
	}

	public static void EnterWalk()
	{
		if ( !Desk.Ready )
			Desk.Boot();
		PlaytestFromStage = false;
		AfterPlay = GameScreen.ModePick;
		PlaySim.Start( Desk.Stage, fromPlaytest: false, walk3d: true );
		Coach.OnWalk();
		Screen = GameScreen.Play3D;
		Bump();
	}

	public static void StartWorldWalk()
	{
		Desk.Autosave( quiet: true );
		PlaytestFromStage = true;
		AfterPlay = GameScreen.World;
		PlaySim.Start( Desk.Stage, fromPlaytest: true, walk3d: true );
		Coach.OnWalk();
		Screen = GameScreen.Play3D;
		Bump();
	}

	public static void OpenStudio( GameScreen studio )
	{
		if ( studio is GameScreen.Play2D or GameScreen.Play3D or GameScreen.Pause or GameScreen.Cleared or GameScreen.Title )
			return;

		Desk.Autosave( quiet: true );
		ResumeTo = studio;
		Screen = studio;
		if ( studio == GameScreen.World )
			WorldStudio.EnsureFramed();
		if ( studio is GameScreen.Stage or GameScreen.Library )
		{
			var start = Desk.Stage?.Find( "start" );
			if ( start is not null && Desk.Stage is not null )
				StageStudio.FocusPlayer( start.Tx * Desk.Stage.TileSize, start.Ty * Desk.Stage.TileSize );
		}
		Lesson.OnStudio( studio );
		Coach.OnStudio( studio );
		Bump();
	}

	public static void StartPlaytest()
	{
		Desk.Autosave( quiet: true );
		PlaytestFromStage = true;
		AfterPlay = GameScreen.Stage;
		PlaySim.Start( Desk.Stage, fromPlaytest: true, dropX: StageStudio.DropX, dropY: StageStudio.DropY );
		Coach.OnPlay();
		Screen = GameScreen.Play2D;
		Bump();
	}

	public static void LeavePlay()
	{
		var to = AfterPlay;
		if ( PlaytestFromStage && to == GameScreen.Stage )
			StageStudio.FocusPlayer( PlaySim.Px, PlaySim.Py );
		if ( PlaytestFromStage && to == GameScreen.World )
			WorldStudio.FocusPlayer( PlaySim.Px, PlaySim.Py, PlaySim.Pz );

		PlaytestFromStage = false;
		Screen = to == GameScreen.Title ? GameScreen.ModePick : to;
		Bump();
	}

	public static void TogglePause()
	{
		if ( Screen == GameScreen.Pause )
		{
			Screen = ResumeTo;
			Bump();
			return;
		}

		if ( Screen == GameScreen.Title || Screen == GameScreen.ModePick )
			return;

		if ( Screen == GameScreen.Cleared )
		{
			LeavePlay();
			return;
		}

		Desk.Autosave( quiet: true );
		ResumeTo = Screen;
		Screen = GameScreen.Pause;
		Bump();
	}

	public static void Resume()
	{
		if ( Screen != GameScreen.Pause )
			return;
		Screen = ResumeTo;
		Bump();
	}

	public static void PlayNextSample()
	{
		var id = StarterPack.NextStageId( Desk.Stage?.Name );
		if ( string.IsNullOrEmpty( id ) || !StarterPack.LoadShelf( id ) )
		{
			LeavePlay();
			return;
		}

		AfterPlay = GameScreen.Stage;
		PlaytestFromStage = true;
		PlaySim.Start( Desk.Stage, fromPlaytest: true );
		Coach.OnPlay();
		Screen = GameScreen.Play2D;
		ShowToast( Desk.Name );
		Bump();
	}

	public static void Replay()
	{
		var walk = PlaySim.Walk3D;
		var after = AfterPlay;
		var fromStudio = after is GameScreen.Stage or GameScreen.World;
		if ( walk )
		{
			PlaySim.Start( Desk.Stage, fromPlaytest: fromStudio, walk3d: true );
			Screen = GameScreen.Play3D;
			Coach.OnWalk();
		}
		else
		{
			var drop = fromStudio;
			PlaySim.Start( Desk.Stage, fromPlaytest: drop, dropX: drop ? StageStudio.DropX : -1, dropY: drop ? StageStudio.DropY : -1 );
			Screen = GameScreen.Play2D;
			Coach.OnPlay();
		}
		Bump();
	}

	public static void OnStageCleared()
	{
		ResumeTo = AfterPlay is GameScreen.Stage or GameScreen.World
			? AfterPlay
			: (PlaySim.Walk3D ? GameScreen.Play3D : GameScreen.Play2D);
		Screen = GameScreen.Cleared;
		Coach.OnCleared();
		Bump();
	}

	public static void Quit()
	{
		Desk.Autosave( quiet: true );
		Game.Close();
	}
}