StarterPack.cs
namespace PixelPusher;
public static class StarterPack
{
public static PixelDoc BlankSprite()
{
return new PixelDoc( 16, 16, Palettes.Default )
{
Name = "coin"
};
}
public static PixelDoc Coin()
{
var doc = new PixelDoc( 16, 16, Palettes.Default ) { Name = "coin" };
FillCircle( doc, 8, 8, 6, 7 );
FillCircle( doc, 8, 8, 5, 14 );
FillCircle( doc, 8, 8, 3, 7 );
Ring( doc, 8, 8, 6, 11 );
return doc;
}
public static TilesetDoc CornerStore()
{
var sheet = new PixelDoc( 128, 64, Palettes.Default ) { Name = "corner_store" };
var set = new TilesetDoc( sheet, 16 )
{
Name = "Corner Store"
};
DrawTile( sheet, 1, Sidewalk );
DrawTile( sheet, 2, Brick );
DrawTile( sheet, 3, Crate );
DrawTile( sheet, 4, Glass );
DrawTile( sheet, 5, ShelfTile );
DrawTile( sheet, 6, Neon );
DrawTile( sheet, 7, Cooler );
DrawTile( sheet, 8, OneWay );
DrawTile( sheet, 9, Spring );
DrawTile( sheet, 10, Hazard );
DrawTile( sheet, 11, BrickDark );
DrawTile( sheet, 12, FloorTan );
DrawTile( sheet, 13, Awning );
DrawTile( sheet, 14, Dumpster );
DrawTile( sheet, 15, Stripe );
DrawTile( sheet, 16, Lamp );
set.Flags[1] = TileFlag.Solid;
set.Flags[2] = TileFlag.Solid;
set.Flags[3] = TileFlag.Solid;
set.Flags[4] = TileFlag.Solid;
set.Flags[5] = TileFlag.Solid;
set.Flags[6] = TileFlag.Solid;
set.Flags[7] = TileFlag.Solid;
set.Flags[8] = TileFlag.OneWay;
set.Flags[9] = TileFlag.Spring;
set.Flags[10] = TileFlag.Hazard;
set.Flags[11] = TileFlag.Solid;
set.Flags[12] = TileFlag.None;
set.Flags[13] = TileFlag.None;
set.Flags[14] = TileFlag.Solid;
set.Flags[15] = TileFlag.None;
set.Flags[16] = TileFlag.None;
return set;
}
public static StageDoc TutorialStage()
{
var stage = new StageDoc( 48, 18, 16 ) { Name = "Stock the Cooler" };
for ( var x = 0; x < stage.Width; x++ )
{
stage.SetTile( x, 16, 1 );
stage.SetTile( x, 17, 2 );
}
for ( var x = 6; x <= 10; x++ )
stage.SetTile( x, 13, 8 );
for ( var x = 14; x <= 18; x++ )
stage.SetTile( x, 11, 5 );
for ( var x = 22; x <= 26; x++ )
stage.SetTile( x, 12, 3 );
for ( var x = 28; x <= 31; x++ )
stage.SetTile( x, 9, 8 );
stage.SetTile( 20, 15, 9 );
stage.SetTile( 33, 15, 10 );
stage.SetTile( 34, 15, 10 );
for ( var y = 12; y <= 15; y++ )
stage.SetTile( 40, y, 7 );
stage.SetTile( 41, 15, 6 );
stage.SetTile( 42, 15, 4 );
stage.SetTile( 43, 15, 4 );
stage.Goal = "grab";
stage.PlaceEntity( "start", 2, 15 );
stage.PlaceEntity( "sign", 4, 15, "STOCK THE COOLER" );
stage.PlaceEntity( "coin", 8, 12 );
stage.PlaceEntity( "coin", 16, 10 );
stage.PlaceEntity( "coin", 29, 8 );
stage.PlaceEntity( "spring", 20, 15 );
stage.PlaceEntity( "check", 21, 15 );
stage.PlaceEntity( "exit", 42, 14 );
return stage;
}
public static StageDoc NightShift()
{
var stage = new StageDoc( 52, 18, 16 ) { Name = "Night Shift", Goal = "exit" };
Ground( stage );
Row( stage, 8, 12, 13, 8 );
Row( stage, 16, 19, 16, 0 );
Row( stage, 16, 19, 17, 0 );
stage.SetTile( 15, 15, 9 );
Row( stage, 21, 25, 14, 3 );
Row( stage, 28, 31, 15, 10 );
Row( stage, 34, 39, 12, 8 );
Row( stage, 41, 44, 10, 5 );
stage.SetTile( 46, 15, 14 );
stage.SetTile( 47, 15, 16 );
Row( stage, 48, 50, 15, 7 );
stage.PlaceEntity( "start", 2, 15 );
stage.PlaceEntity( "sign", 4, 15, "NIGHT SHIFT" );
stage.PlaceEntity( "coin", 10, 12 );
stage.PlaceEntity( "spring", 15, 15 );
stage.PlaceEntity( "check", 22, 13 );
stage.PlaceEntity( "coin", 36, 11 );
stage.PlaceEntity( "coin", 42, 9 );
stage.PlaceEntity( "exit", 49, 14 );
return stage;
}
public static StageDoc BackLot()
{
var stage = new StageDoc( 44, 16, 16 ) { Name = "Back Lot", Goal = "grab" };
Ground( stage, 14, 15 );
Row( stage, 10, 13, 14, 0 );
Row( stage, 10, 13, 15, 0 );
Row( stage, 20, 23, 14, 0 );
Row( stage, 20, 23, 15, 0 );
Row( stage, 30, 33, 14, 0 );
Row( stage, 30, 33, 15, 0 );
stage.SetTile( 9, 13, 9 );
stage.SetTile( 19, 13, 9 );
stage.SetTile( 29, 13, 9 );
Row( stage, 14, 17, 11, 8 );
stage.SetTile( 36, 13, 14 );
stage.SetTile( 37, 13, 13 );
stage.PlaceEntity( "start", 2, 13 );
stage.PlaceEntity( "sign", 4, 13, "BACK LOT" );
stage.PlaceEntity( "coin", 11, 10 );
stage.PlaceEntity( "coin", 21, 9 );
stage.PlaceEntity( "coin", 31, 9 );
stage.PlaceEntity( "spring", 9, 13 );
stage.PlaceEntity( "spring", 19, 13 );
stage.PlaceEntity( "check", 24, 13 );
stage.PlaceEntity( "exit", 40, 12 );
return stage;
}
public static StageDoc ClosedSundays()
{
var stage = new StageDoc( 36, 22, 16 ) { Name = "Closed Sundays", Goal = "exit" };
Ground( stage, 20, 21 );
Row( stage, 6, 9, 17, 3 );
Row( stage, 12, 16, 15, 8 );
Row( stage, 4, 7, 13, 5 );
Row( stage, 18, 22, 12, 3 );
Row( stage, 24, 28, 10, 8 );
Row( stage, 14, 17, 8, 5 );
Row( stage, 29, 33, 7, 7 );
stage.SetTile( 10, 19, 9 );
stage.SetTile( 23, 19, 10 );
stage.SetTile( 24, 19, 10 );
stage.SetTile( 8, 16, 16 );
stage.SetTile( 20, 11, 13 );
stage.PlaceEntity( "start", 2, 19 );
stage.PlaceEntity( "sign", 4, 19, "CLOSED SUNDAYS" );
stage.PlaceEntity( "coin", 7, 16 );
stage.PlaceEntity( "coin", 14, 14 );
stage.PlaceEntity( "coin", 20, 11 );
stage.PlaceEntity( "coin", 26, 9 );
stage.PlaceEntity( "spring", 10, 19 );
stage.PlaceEntity( "spring", 27, 9 );
stage.PlaceEntity( "check", 19, 11 );
stage.PlaceEntity( "exit", 31, 6 );
return stage;
}
public static readonly (string Id, string Title, string Kind, string When)[] Shelf =
{
("starter", "Stock the Cooler", "pixel.project", "tour"),
("night-shift", "Night Shift", "pixel.stage", "sample"),
("back-lot", "Back Lot", "pixel.stage", "sample"),
("closed-sundays", "Closed Sundays", "pixel.stage", "sample"),
("corner-store", "Corner Store", "pixel.tileset", "tiles"),
("coin", "Coin", "pixel.sprite", "sprite")
};
public static string NextStageId( string name )
{
var order = new[] { "Stock the Cooler", "Night Shift", "Back Lot", "Closed Sundays" };
var i = Array.IndexOf( order, name );
if ( i < 0 || i >= order.Length - 1 )
return null;
return i switch
{
0 => "night-shift",
1 => "back-lot",
2 => "closed-sundays",
_ => null
};
}
public static bool LoadShelf( string id )
{
switch ( id )
{
case "starter":
Desk.Sprite = Coin();
Desk.Tileset = CornerStore();
Desk.Stage = TutorialStage();
Desk.Chip = new ChipDoc();
Desk.Name = "Stock the Cooler";
break;
case "night-shift":
EnsureKit();
Desk.Stage = NightShift();
Desk.Name = Desk.Stage.Name;
break;
case "back-lot":
EnsureKit();
Desk.Stage = BackLot();
Desk.Name = Desk.Stage.Name;
break;
case "closed-sundays":
EnsureKit();
Desk.Stage = ClosedSundays();
Desk.Name = Desk.Stage.Name;
break;
case "corner-store":
Desk.Tileset = CornerStore();
Desk.Name = "Corner Store";
break;
case "coin":
Desk.Sprite = Coin();
Desk.Name = "Coin";
break;
default:
return false;
}
Desk.Touch();
return true;
}
static void EnsureKit()
{
Desk.Tileset ??= CornerStore();
Desk.Sprite ??= Coin();
Desk.Chip ??= new ChipDoc();
}
static void Ground( StageDoc stage, int walk = 16, int fill = 17 )
{
for ( var x = 0; x < stage.Width; x++ )
{
stage.SetTile( x, walk, 1 );
if ( fill < stage.Height )
stage.SetTile( x, fill, 2 );
}
}
static void Row( StageDoc stage, int x0, int x1, int y, int id )
{
for ( var x = x0; x <= x1; x++ )
stage.SetTile( x, y, id );
}
public static void BlitTerry( Color32[] dst, int dw, int dh, int dx, int dy, bool faceLeft, int frame )
{
var pal = Palettes.Default.Colors;
var src = frame == 1 ? TerryWalk : TerryIdle;
for ( var y = 0; y < 16; y++ )
for ( var x = 0; x < 16; x++ )
{
var sx = faceLeft ? 15 - x : x;
var i = src[y * 16 + sx];
if ( i == 0 )
continue;
var px = dx + x;
var py = dy + y;
if ( (uint)px >= (uint)dw || (uint)py >= (uint)dh )
continue;
dst[py * dw + px] = pal[i];
}
}
public static void BlitCoin( Color32[] dst, int dw, int dh, int dx, int dy, float t )
{
var pal = Palettes.Default.Colors;
var bob = (int)MathF.Round( MathF.Sin( t * 6f ) );
for ( var y = 0; y < 16; y++ )
for ( var x = 0; x < 16; x++ )
{
var i = CoinPx[y * 16 + x];
if ( i == 0 )
continue;
var px = dx + x;
var py = dy + y + bob;
if ( (uint)px >= (uint)dw || (uint)py >= (uint)dh )
continue;
dst[py * dw + px] = pal[i];
}
}
static readonly byte[] TerryIdle = BuildTerry( false );
static readonly byte[] TerryWalk = BuildTerry( true );
static readonly byte[] CoinPx = BuildCoin();
static byte[] BuildTerry( bool walk )
{
var p = new byte[16 * 16];
void D( int x, int y, byte c )
{
if ( (uint)x < 16 && (uint)y < 16 )
p[y * 16 + x] = c;
}
for ( var y = 1; y <= 5; y++ )
for ( var x = 5; x <= 10; x++ )
D( x, y, 7 );
D( 6, 3, 1 );
D( 9, 3, 1 );
D( 7, 4, 11 );
for ( var y = 6; y <= 10; y++ )
for ( var x = 5; x <= 10; x++ )
D( x, y, 12 );
D( 4, 7, 6 );
D( 11, 7, 6 );
var ly = walk ? 12 : 11;
var ry = walk ? 11 : 12;
D( 6, ly, 3 );
D( 6, ly + 1, 3 );
D( 6, ly + 2, 9 );
D( 9, ry, 3 );
D( 9, ry + 1, 3 );
D( 9, ry + 2, 9 );
return p;
}
static byte[] BuildCoin()
{
var doc = Coin();
return PixelDoc.CopyBytes( doc.Indices );
}
delegate void TilePainter( PixelDoc doc, int ox, int oy );
static void DrawTile( PixelDoc sheet, int id, TilePainter paint )
{
var col = 8;
var ox = (id % col) * 16;
var oy = (id / col) * 16;
paint( sheet, ox, oy );
}
static void Sidewalk( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy, 16, 16, 15 );
HLine( d, ox, oy, 16, 5 );
HLine( d, ox, oy + 8, 16, 4 );
VLine( d, ox + 8, oy, 16, 4 );
}
static void Brick( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy, 16, 16, 11 );
HLine( d, ox, oy + 4, 16, 10 );
HLine( d, ox, oy + 9, 16, 10 );
HLine( d, ox, oy + 14, 16, 10 );
VLine( d, ox + 7, oy, 5, 10 );
VLine( d, ox + 3, oy + 5, 5, 10 );
VLine( d, ox + 12, oy + 10, 5, 10 );
}
static void BrickDark( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy, 16, 16, 10 );
HLine( d, ox, oy + 5, 16, 1 );
HLine( d, ox, oy + 11, 16, 1 );
}
static void Crate( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy, 16, 16, 11 );
Rect( d, ox, oy, 15, 15, 7 );
d.SetIndex( ox + 8, oy + 8, 14 );
}
static void Glass( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy, 16, 16, 12 );
for ( var i = 2; i < 14; i++ )
d.SetIndex( ox + i, oy + i / 2, 5 );
Rect( d, ox, oy, 15, 15, 4 );
}
static void ShelfTile( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy, 16, 16, 10 );
HLine( d, ox, oy + 3, 16, 11 );
HLine( d, ox, oy + 8, 16, 11 );
HLine( d, ox, oy + 13, 16, 11 );
d.SetIndex( ox + 4, oy + 6, 6 );
d.SetIndex( ox + 11, oy + 11, 8 );
}
static void Neon( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy, 16, 16, 1 );
Rect( d, ox + 1, oy + 1, 13, 13, 6 );
Fill( d, ox + 3, oy + 3, 10, 10, 6 );
HLine( d, ox + 2, oy + 8, 12, 9 );
}
static void Cooler( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy, 16, 16, 3 );
Rect( d, ox, oy, 15, 15, 12 );
Fill( d, ox + 3, oy + 3, 10, 8, 12 );
HLine( d, ox + 3, oy + 12, 10, 8 );
}
static void OneWay( PixelDoc d, int ox, int oy )
{
HLine( d, ox, oy + 4, 16, 8 );
HLine( d, ox, oy + 5, 16, 13 );
for ( var x = 0; x < 16; x += 4 )
d.SetIndex( ox + x, oy + 3, 8 );
}
static void Spring( PixelDoc d, int ox, int oy )
{
Fill( d, ox + 3, oy + 10, 10, 6, 6 );
HLine( d, ox + 2, oy + 9, 12, 9 );
HLine( d, ox + 4, oy + 6, 8, 6 );
HLine( d, ox + 5, oy + 3, 6, 9 );
}
static void Hazard( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy + 8, 16, 8, 11 );
for ( var x = 0; x < 16; x += 2 )
d.SetIndex( ox + x, oy + 7 - (x % 4), 6 );
}
static void FloorTan( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy, 16, 16, 11 );
for ( var y = 0; y < 16; y += 4 )
for ( var x = (y / 4) % 2; x < 16; x += 2 )
d.SetIndex( ox + x, oy + y, 7 );
}
static void Awning( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy + 4, 16, 6, 6 );
HLine( d, ox, oy + 3, 16, 9 );
HLine( d, ox, oy + 10, 16, 10 );
for ( var x = 0; x < 16; x += 4 )
VLine( d, ox + x, oy + 4, 6, 8 );
}
static void Dumpster( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy + 4, 16, 12, 3 );
Rect( d, ox, oy + 4, 15, 11, 12 );
HLine( d, ox + 2, oy + 3, 12, 5 );
d.SetIndex( ox + 3, oy + 8, 8 );
d.SetIndex( ox + 12, oy + 8, 8 );
}
static void Stripe( PixelDoc d, int ox, int oy )
{
Fill( d, ox, oy, 16, 16, 2 );
HLine( d, ox, oy + 7, 16, 7 );
HLine( d, ox, oy + 8, 16, 7 );
}
static void Lamp( PixelDoc d, int ox, int oy )
{
VLine( d, ox + 8, oy + 6, 10, 4 );
Fill( d, ox + 5, oy + 1, 6, 5, 7 );
d.SetIndex( ox + 8, oy, 14 );
}
static void Fill( PixelDoc d, int x, int y, int w, int h, byte c )
{
for ( var yy = 0; yy < h; yy++ )
for ( var xx = 0; xx < w; xx++ )
d.SetIndex( x + xx, y + yy, c );
}
static void HLine( PixelDoc d, int x, int y, int w, byte c )
{
for ( var i = 0; i < w; i++ )
d.SetIndex( x + i, y, c );
}
static void VLine( PixelDoc d, int x, int y, int h, byte c )
{
for ( var i = 0; i < h; i++ )
d.SetIndex( x, y + i, c );
}
static void Rect( PixelDoc d, int x, int y, int w, int h, byte c )
{
HLine( d, x, y, w, c );
HLine( d, x, y + h, w, c );
VLine( d, x, y, h, c );
VLine( d, x + w, y, h, c );
}
static void FillCircle( PixelDoc d, int cx, int cy, int r, byte c )
{
var r2 = r * r;
for ( var y = -r; y <= r; y++ )
for ( var x = -r; x <= r; x++ )
{
if ( x * x + y * y <= r2 )
d.SetIndex( cx + x, cy + y, c );
}
}
static void Ring( PixelDoc d, int cx, int cy, int r, byte c )
{
for ( var a = 0; a < 360; a += 6 )
{
var rad = a * MathF.PI / 180f;
var x = cx + (int)MathF.Round( MathF.Cos( rad ) * r );
var y = cy + (int)MathF.Round( MathF.Sin( rad ) * r );
d.SetIndex( x, y, c );
}
}
}