ChipStudio.cs
namespace PixelPusher;
public static class ChipStudio
{
public static readonly string[] ChannelNames = { "PULSE", "TRI", "NOISE", "DRUM" };
public static readonly string[] NoteNames = { "", "C", "CS", "D", "DS", "E", "F", "FS", "G", "GS", "A", "AS", "B" };
public static readonly bool[] Mute = { false, false, false, false };
public static int CursorCh;
public static int CursorStep;
public static bool Playing;
public static int Head;
public static bool PadAim;
static float _stepT;
static float _padAccX;
static float _padAccY;
public static ChipDoc Doc => Desk.Chip;
public static void TogglePlay()
{
Playing = !Playing;
if ( Playing )
{
Head = 0;
_stepT = 0;
Fire( 0 );
}
GameFlow.Bump();
}
public static void Clear()
{
Doc?.Clear();
Desk.Touch();
GameFlow.Bump();
}
public static void SetBpm( int bpm )
{
if ( Doc is null )
return;
Doc.Bpm = Math.Clamp( bpm, 60, 240 );
Desk.Touch();
GameFlow.Bump();
}
public const int GridX = 88;
public const int GridY = 72;
public const int CellW = 32;
public const int CellH = 62;
public const int LabelW = 80;
public static void ToggleMute( int ch )
{
if ( (uint)ch >= ChipDoc.Channels )
return;
Mute[ch] = !Mute[ch];
GameFlow.Bump();
}
public static void DragPaint( int mx, int my, bool erase )
{
var ch = (my - GridY) / CellH;
var step = (mx - GridX) / CellW;
if ( DeskNet.GuestLocked )
return;
if ( (uint)ch >= ChipDoc.Channels || (uint)step >= ChipDoc.Steps || Doc is null )
return;
CursorCh = ch;
CursorStep = step;
var want = erase ? (byte)0 : (byte)8;
if ( Doc.Get( ch, step ) == want )
return;
if ( erase )
Doc.Set( ch, step, 0 );
else if ( Doc.Get( ch, step ) == 0 )
Doc.Set( ch, step, 8 );
Desk.Touch();
GameFlow.Bump();
}
public static void Hit( int mx, int my, bool right )
{
var ch = (my - GridY) / CellH;
if ( (uint)ch >= ChipDoc.Channels )
return;
if ( mx < GridX )
{
ToggleMute( ch );
return;
}
var step = (mx - GridX) / CellW;
if ( (uint)step >= ChipDoc.Steps )
return;
Click( ch, step, right );
}
public static void NudgeCursor( int dir )
{
if ( Doc is null )
return;
Doc.Nudge( CursorCh, CursorStep, dir );
Desk.Touch();
ChipVoices.Tone( CursorCh, Doc.Get( CursorCh, CursorStep ) );
GameFlow.Bump();
}
public static void StepCursor( int dir )
{
CursorStep = Math.Clamp( CursorStep + dir, 0, ChipDoc.Steps - 1 );
GameFlow.Bump();
}
public static void Click( int ch, int step, bool right )
{
if ( Doc is null )
return;
if ( DeskNet.GuestLocked )
return;
CursorCh = ch;
CursorStep = step;
if ( right )
Doc.Nudge( ch, step, 1 );
else
Doc.Toggle( ch, step );
Desk.Touch();
ChipVoices.Tone( ch, Doc.Get( ch, step ) );
GameFlow.Bump();
}
public static void Tick()
{
if ( GameFlow.Screen != GameScreen.Chip || Doc is null )
return;
if ( Input.Pressed( "Jump" ) )
TogglePlay();
if ( Input.Pressed( "Reload" ) )
Doc.Nudge( CursorCh, CursorStep, -1 );
if ( Input.Pressed( "Use" ) )
Click( CursorCh, CursorStep, false );
if ( Input.Pressed( "View" ) )
Click( CursorCh, CursorStep, true );
TickPad();
TickPlay();
}
static void TickPad()
{
var move = Input.AnalogMove;
var ax = move.y;
var ay = -move.x;
if ( MathF.Abs( ax ) < 0.4f ) ax = 0;
if ( MathF.Abs( ay ) < 0.4f ) ay = 0;
if ( ax == 0 && ay == 0 )
{
_padAccX = 0;
_padAccY = 0;
}
else
{
PadAim = true;
_padAccX += ax * 10f * Time.Delta;
_padAccY += ay * 8f * Time.Delta;
while ( MathF.Abs( _padAccX ) >= 1f )
{
CursorStep = Math.Clamp( CursorStep + Math.Sign( _padAccX ), 0, ChipDoc.Steps - 1 );
_padAccX -= Math.Sign( _padAccX );
GameFlow.Bump();
}
while ( MathF.Abs( _padAccY ) >= 1f )
{
CursorCh = Math.Clamp( CursorCh + Math.Sign( _padAccY ), 0, ChipDoc.Channels - 1 );
_padAccY -= Math.Sign( _padAccY );
GameFlow.Bump();
}
}
if ( Input.Pressed( "SlotPrev" ) )
{
CursorStep = Math.Max( 0, CursorStep - 1 );
GameFlow.Bump();
}
if ( Input.Pressed( "SlotNext" ) )
{
CursorStep = Math.Min( ChipDoc.Steps - 1, CursorStep + 1 );
GameFlow.Bump();
}
}
static void TickPlay()
{
if ( !Playing )
return;
var bpm = Math.Clamp( Doc.Bpm, 60, 240 );
var stepSec = 60f / bpm / 4f;
_stepT += Time.Delta;
if ( _stepT < stepSec )
return;
_stepT -= stepSec;
Head = (Head + 1) % ChipDoc.Steps;
Fire( Head );
GameFlow.Bump();
}
static void Fire( int step )
{
for ( var ch = 0; ch < ChipDoc.Channels; ch++ )
{
if ( Mute[ch] )
continue;
ChipVoices.Tone( ch, Doc.Get( ch, step ) );
}
}
}