StudioMouse.cs

The static StudioMouse class manages user input for an integrated pixel art and level design editor. It maps mouse wheel and modifier key inputs to actions like zooming, panning, selection, and tool usage based on the current editor view.

namespace PixelPusher;

public static class StudioMouse
{
	static float _wheelAt;

	public static void Wheel( int dir, int vx, int vy )
	{
		if ( dir == 0 )
			return;
		_wheelAt = Time.Now;
		dir = dir > 0 ? 1 : -1;

		switch ( GameFlow.Screen )
		{
			case GameScreen.Pixel:
				if ( Ctrl() ) PixelStudio.SetBrush( PixelStudio.Brush + dir );
				else if ( Shift() ) { if ( dir > 0 ) PixelStudio.NextFrame(); else PixelStudio.PrevFrame(); }
				else if ( Alt() ) CycleColor( dir );
				else PixelStudio.ZoomAt( vx, vy, dir );
				break;
			case GameScreen.Stage:
			case GameScreen.Library:
				if ( Ctrl() ) StageStudio.SelectTile( StageStudio.TileId + dir );
				else if ( Shift() ) StageStudio.CycleStamp( dir );
				else StageStudio.ZoomAt( vx, vy, dir );
				break;
			case GameScreen.World:
				WorldStudio.SetZoom( WorldStudio.Zoom + dir );
				break;
			case GameScreen.Chip:
				if ( Ctrl() ) ChipStudio.SetBpm( (Desk.Chip?.Bpm ?? 112) + dir * 8 );
				else ChipStudio.NudgeCursor( dir );
				break;
		}
	}

	public static void Back()
	{
		switch ( GameFlow.Screen )
		{
			case GameScreen.Pixel:
				PixelStudio.Undo();
				break;
			case GameScreen.Stage:
				StageStudio.Undo();
				break;
			case GameScreen.Chip:
				ChipStudio.StepCursor( -1 );
				break;
		}
	}

	public static void Forward()
	{
		switch ( GameFlow.Screen )
		{
			case GameScreen.Pixel:
				PixelStudio.Redo();
				break;
			case GameScreen.Stage:
				StageStudio.Redo();
				break;
			case GameScreen.Chip:
				ChipStudio.StepCursor( 1 );
				break;
		}
	}

	public static void Pan( int dx, int dy )
	{
		if ( dx == 0 && dy == 0 )
			return;
		switch ( GameFlow.Screen )
		{
			case GameScreen.Pixel:
				PixelStudio.PanX -= dx;
				PixelStudio.PanY -= dy;
				GameFlow.Bump();
				break;
			case GameScreen.Stage:
			case GameScreen.Library:
			{
				var z = Math.Max( 1, StageStudio.Zoom );
				StageStudio.Pan( -dx / (float)z, -dy / (float)z );
				GameFlow.Bump();
				break;
			}
			case GameScreen.World:
				WorldStudio.Pan( -dx, -dy );
				GameFlow.Bump();
				break;
		}
	}

	public static void Tick()
	{
		if ( !GameFlow.InStudio && GameFlow.Screen != GameScreen.Library )
			return;
		if ( Input.Pressed( "EditorBack" ) )
			Back();
		if ( Input.Pressed( "EditorForward" ) )
			Forward();
		if ( MoveKeys.ZoomIn )
			Wheel( 1, CrtView.HoverX, CrtView.HoverY );
		if ( MoveKeys.ZoomOut )
			Wheel( -1, CrtView.HoverX, CrtView.HoverY );
		if ( MoveKeys.Home )
			Recenter();

		if ( Time.Now - _wheelAt < 0.02f || !CrtView.PointerOn )
			return;
		try
		{
			var w = Input.MouseWheel.y;
			if ( MathF.Abs( w ) > 0.01f )
				Wheel( w > 0 ? 1 : -1, CrtView.HoverX, CrtView.HoverY );
		}
		catch
		{
			// no wheel
		}
	}

	static void CycleColor( int dir )
	{
		var n = Desk.Sprite?.Palette?.Length ?? 16;
		if ( n <= 0 )
			return;
		var next = PixelStudio.Fg + dir;
		while ( next < 0 ) next += n;
		PixelStudio.SelectColor( (byte)(next % n) );
	}

	static void Recenter()
	{
		switch ( GameFlow.Screen )
		{
			case GameScreen.Pixel:
				PixelStudio.PanX = 0;
				PixelStudio.PanY = 0;
				GameFlow.Bump();
				break;
			case GameScreen.Stage:
			case 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 );
				else
				{
					StageStudio.CamX = 0;
					StageStudio.CamY = 0;
				}
				GameFlow.Bump();
				break;
			}
			case GameScreen.World:
				WorldStudio.CamX = 0;
				WorldStudio.CamY = 0;
				WorldStudio.EnsureFramed();
				GameFlow.Bump();
				break;
		}
	}

	public static bool AltHeld => Alt();
	public static bool CtrlHeld => Ctrl();

	static bool Ctrl()
	{
		try
		{
			if ( Input.Keyboard.Down( "CTRL" ) || Input.Keyboard.Down( "RCTRL" ) )
				return true;
		}
		catch
		{
			// ignore
		}
		return Input.Down( "Duck" );
	}

	static bool Shift()
	{
		try
		{
			if ( Input.Keyboard.Down( "SHIFT" ) || Input.Keyboard.Down( "RSHIFT" ) )
				return true;
		}
		catch
		{
			// ignore
		}
		return Input.Down( "Run" );
	}

	static bool Alt()
	{
		try
		{
			if ( Input.Keyboard.Down( "ALT" ) || Input.Keyboard.Down( "RALT" ) )
				return true;
		}
		catch
		{
			// ignore
		}
		return Input.Down( "Walk" );
	}
}