Hud.razor
@using System
@using Sandbox
@using Sandbox.UI
@inherits PanelComponent
@namespace PixelPusher
<root class="root">
@if ( GameFlow.Screen == GameScreen.Title )
{
<div class="screen title">
<div class="neon-frame">
<div class="kicker">CORNER STORE HUB</div>
<div class="logo">PIXEL<br/>PUSHER</div>
<div class="tag">@GameInfo.Tagline</div>
<div class="menu-links">
@if ( !Lesson.Finished )
{
<div class="btn primary" @onclick="@OnTour" @onmouseover="@OnHover">TAKE THE TOUR</div>
<div class="btn" @onclick="@OnBuild" @onmouseover="@OnHover">BUILD</div>
}
else
{
<div class="btn primary" @onclick="@OnBuild" @onmouseover="@OnHover">BUILD</div>
<div class="btn quiet" @onclick="@OnTour" @onmouseover="@OnHover">TOUR AGAIN</div>
}
<div class="btn" @onclick="@OnPlay" @onmouseover="@OnHover">PLAY</div>
<div class="btn quiet" @onclick="@OnDesk" @onmouseover="@OnHover">DESK</div>
<div class="btn quiet danger" @onclick="@OnQuit" @onmouseover="@OnHover">QUIT</div>
</div>
<div class="ver">@GameInfo.Package · @GameInfo.VersionLabel · @GameInfo.Rank</div>
</div>
</div>
}
else if ( GameFlow.Screen == GameScreen.ModePick )
{
<div class="screen title">
<div class="neon-frame slim">
<div class="kicker">PICK A DOOR</div>
<div class="logo small">PIXEL PUSHER</div>
<div class="menu-links">
<div class="btn primary" @onclick="@OnBuild" @onmouseover="@OnHover">BUILD</div>
<div class="btn" @onclick="@OnPlay" @onmouseover="@OnHover">PLAY 2D</div>
<div class="btn quiet" @onclick="@OnWalk" @onmouseover="@OnHover">WALK 3D · LATER</div>
<div class="btn quiet" @onclick="@OnTitle" @onmouseover="@OnHover">BACK</div>
</div>
</div>
</div>
}
else if ( GameFlow.Screen == GameScreen.Pause )
{
<div class="screen pause">
<div class="neon-frame slim">
<div class="kicker">PAUSED · DRAFT KEPT</div>
<div class="menu-links">
<div class="btn primary" @onclick="@OnResume" @onmouseover="@OnHover">RESUME</div>
<div class="btn" @onclick="@OnBuild" @onmouseover="@OnHover">BUILD</div>
<div class="btn" @onclick="@OnPlay" @onmouseover="@OnHover">PLAY</div>
<div class="btn quiet" @onclick="@OnTitle" @onmouseover="@OnHover">HUB</div>
</div>
</div>
</div>
}
else if ( GameFlow.Screen == GameScreen.Cleared )
{
<div class="screen pause">
<div class="neon-frame slim">
<div class="kicker">STAGE CLEARED</div>
<div class="logo small">NICE PUSH</div>
<div class="tag">@PlaySim.TimeText @PlaySim.Coins/@PlaySim.CoinTotal coins @PlaySim.Deaths falls</div>
<div class="menu-links">
@if ( !string.IsNullOrEmpty( StarterPack.NextStageId( Desk.Stage?.Name ) ) )
{
<div class="btn primary" @onclick="@OnNextStage" @onmouseover="@OnHover">NEXT STAGE</div>
<div class="btn" @onclick="@OnPublish" @onmouseover="@OnHover">PUBLISH</div>
}
else
{
<div class="btn primary" @onclick="@OnPublish" @onmouseover="@OnHover">PUBLISH</div>
}
<div class="btn" @onclick="@OnLeavePlay" @onmouseover="@OnHover">BACK</div>
<div class="btn quiet" @onclick="@OnAgain" @onmouseover="@OnHover">AGAIN</div>
</div>
</div>
</div>
}
else
{
<div class="shell">
<div class="topbar">
<div class="brand">PIXEL PUSHER</div>
<div class="tabs">
<div class="tab @(GameFlow.Screen == GameScreen.Pixel ? "on" : "")" @onclick="@(() => Open(GameScreen.Pixel))" @onmouseover="@OnHover">PIXEL</div>
<div class="tab @(GameFlow.Screen == GameScreen.Stage ? "on" : "") @(Lesson.Highlight == "tab-stage" ? "glow" : "")" @onclick="@(() => Open(GameScreen.Stage))" @onmouseover="@OnHover">STAGE</div>
<div class="tab later @(GameFlow.Screen == GameScreen.World ? "on" : "")" @onclick="@(() => Open(GameScreen.World))" @onmouseover="@OnHover">WORLD</div>
<div class="tab @(GameFlow.Screen == GameScreen.Chip ? "on" : "")" @onclick="@(() => Open(GameScreen.Chip))" @onmouseover="@OnHover">CHIP</div>
<div class="tab @(GameFlow.Screen == GameScreen.Library ? "on" : "")" @onclick="@(() => Open(GameScreen.Library))" @onmouseover="@OnHover">LIBRARY</div>
</div>
<div class="top-actions">
@if ( GameFlow.Screen == GameScreen.Stage )
{
<div class="btn slim primary @(Lesson.Highlight == "drop-in" ? "glow" : "")" @onclick="@OnDropIn" @onmouseover="@OnHover">DROP IN</div>
}
@if ( GameFlow.Screen == GameScreen.World )
{
<div class="btn slim primary" @onclick="@OnWalkStudio" @onmouseover="@OnHover">WALK</div>
}
@if ( GameFlow.Screen is GameScreen.Play2D or GameScreen.Play3D )
{
<div class="stat">@PlaySim.Coins/@PlaySim.CoinTotal</div>
<div class="stat">@PlaySim.TimeText</div>
<div class="btn slim" @onclick="@OnLeavePlay" @onmouseover="@OnHover">EDIT</div>
}
@if ( DeskNet.Live )
{
<div class="stat">@DeskNet.Presence</div>
@if ( !DeskNet.Host )
{
@if ( DeskNet.Watching )
{
<div class="btn slim" @onclick="@DeskNet.Fork" @onmouseover="@OnHover">FORK</div>
<div class="btn slim" @onclick="@DeskNet.MyDesk" @onmouseover="@OnHover">MY DESK</div>
}
else
{
<div class="btn slim primary" @onclick="@DeskNet.Watch" @onmouseover="@OnHover">WATCH</div>
<div class="btn slim" @onclick="@DeskNet.DropOnDesk" @onmouseover="@OnHover">DROP ON DESK</div>
}
}
}
<div class="btn slim" @onclick="@OnSave" @onmouseover="@OnHover">SAVE</div>
</div>
</div>
<div class="work">
@if ( GameFlow.Screen == GameScreen.Pixel )
{
<div class="rail">
<div class="rail-title">TOOLS</div>
<div class="tool @(PixelStudio.Tool == PushTool.Push ? "on" : "")" @onclick="@(() => PixelStudio.SelectTool(PushTool.Push))">PUSH</div>
<div class="tool @(PixelStudio.Tool == PushTool.Flood ? "on" : "")" @onclick="@(() => PixelStudio.SelectTool(PushTool.Flood))">FLOOD</div>
<div class="tool @(PixelStudio.Tool == PushTool.Sip ? "on" : "")" @onclick="@(() => PixelStudio.SelectTool(PushTool.Sip))">SIP</div>
<div class="tool @(PixelStudio.Tool == PushTool.Block ? "on" : "")" @onclick="@(() => PixelStudio.SelectTool(PushTool.Block))">BLOCK</div>
<div class="tool @(PixelStudio.Tool == PushTool.Line ? "on" : "")" @onclick="@(() => PixelStudio.SelectTool(PushTool.Line))">LINE</div>
<div class="tool @(PixelStudio.Tool == PushTool.Oval ? "on" : "")" @onclick="@(() => PixelStudio.SelectTool(PushTool.Oval))">OVAL</div>
<div class="row">
<div class="tool slim @(PixelStudio.Brush == 1 ? "on" : "")" @onclick="@(() => PixelStudio.SetBrush(1))">1</div>
<div class="tool slim @(PixelStudio.Brush == 2 ? "on" : "")" @onclick="@(() => PixelStudio.SetBrush(2))">2</div>
<div class="tool slim @(PixelStudio.Brush == 3 ? "on" : "")" @onclick="@(() => PixelStudio.SetBrush(3))">3</div>
</div>
<div class="row">
<div class="tool slim @(PixelStudio.FillShape ? "on" : "")" @onclick="@PixelStudio.ToggleFill">FILL</div>
<div class="tool slim @(PixelStudio.Circle ? "on" : "")" @onclick="@PixelStudio.ToggleCircle">@(PixelStudio.Circle ? "CIRC" : "SQR")</div>
</div>
<div class="row">
<div class="tool slim" @onclick="@(() => PixelStudio.SetZoom(PixelStudio.Zoom - 1))">−</div>
<div class="hint">@PixelStudio.Zoom x</div>
<div class="tool slim" @onclick="@(() => PixelStudio.SetZoom(PixelStudio.Zoom + 1))">+</div>
</div>
<div class="tool slim" @onclick="@PixelStudio.Undo">UNDO</div>
<div class="tool slim" @onclick="@PixelStudio.Redo">REDO</div>
<div class="tool slim" @onclick="@PixelStudio.Clear">CLEAR</div>
<div class="row wrap">
<div class="tool slim @(PixelStudio.Mirror ? "on" : "")" @onclick="@PixelStudio.ToggleMirror">MIRROR</div>
<div class="tool slim" @onclick="@PixelStudio.FlipH">FLIP H</div>
<div class="tool slim" @onclick="@PixelStudio.FlipV">FLIP V</div>
<div class="tool slim" @onclick="@PixelStudio.Rotate">ROT 90</div>
<div class="tool slim" @onclick="@PixelStudio.ReplaceUnderCursor">SWAP ALL</div>
</div>
<div class="rail-title">ANIM</div>
<div class="row">
<div class="tool slim" @onclick="@PixelStudio.PrevFrame"><</div>
<div class="hint">@( (Desk.Sprite?.Frame ?? 0) + 1 )/@(Desk.Sprite?.FrameCount ?? 1)</div>
<div class="tool slim" @onclick="@PixelStudio.NextFrame">></div>
</div>
<div class="row wrap">
<div class="tool slim" @onclick="@PixelStudio.AddFrame">+ FRAME</div>
<div class="tool slim" @onclick="@PixelStudio.DeleteFrame">DEL</div>
<div class="tool slim @(PixelStudio.Onion ? "on" : "")" @onclick="@PixelStudio.ToggleOnion">ONION</div>
<div class="tool slim @(PixelStudio.Playing ? "on" : "")" @onclick="@PixelStudio.TogglePlay">PLAY</div>
</div>
<div class="hint">Alt sip H/V flip R rot M mirror P swap-all</div>
<div class="rail-title">TRACE</div>
<TextEntry class="ref-url" @ref="_traceUrl"></TextEntry>
<div class="tool slim" @onclick="@LoadTrace">LOAD URL</div>
<div class="row wrap">
<div class="tool slim @(PixelRef.Visible ? "on" : "")" @onclick="@PixelRef.Toggle">@(PixelRef.Visible ? "ON" : "OFF")</div>
<div class="tool slim @(PixelRef.Fit ? "on" : "")" @onclick="@PixelRef.ToggleFit">@(PixelRef.Fit ? "FIT" : "1:1")</div>
<div class="tool slim" @onclick="@(() => PixelRef.NudgeOpacity(-5))">−</div>
<div class="hint">@PixelRef.Opacity%</div>
<div class="tool slim" @onclick="@(() => PixelRef.NudgeOpacity(5))">+</div>
</div>
<div class="tool slim" @onclick="@PixelRef.Drop">DROP</div>
<div class="hint">@PixelRef.Status · never saved</div>
<div class="rail-title">SIZE</div>
<div class="row wrap">
@foreach ( var n in Sizes )
{
var size = n;
<div class="tool slim @(Desk.Sprite is not null && Desk.Sprite.Width == size ? "on" : "")" @onclick="@(() => PixelStudio.NewCanvas(size))">@size</div>
}
</div>
<div class="rail-title">USE AS</div>
<div class="row wrap">
<div class="tool slim @(PixelStudio.Role == StampRole.Decor ? "on" : "")" @onclick="@(() => PixelStudio.SetRole(StampRole.Decor))">DECOR</div>
<div class="tool slim @(PixelStudio.Role == StampRole.Solid ? "on" : "")" @onclick="@(() => PixelStudio.SetRole(StampRole.Solid))">SOLID</div>
<div class="tool slim @(PixelStudio.Role == StampRole.OneWay ? "on" : "")" @onclick="@(() => PixelStudio.SetRole(StampRole.OneWay))">ONE</div>
<div class="tool slim @(PixelStudio.Role == StampRole.Hazard ? "on" : "")" @onclick="@(() => PixelStudio.SetRole(StampRole.Hazard))">HAZARD</div>
<div class="tool slim @(PixelStudio.Role == StampRole.Spring ? "on" : "")" @onclick="@(() => PixelStudio.SetRole(StampRole.Spring))">SPRING</div>
</div>
<div class="tool" @onclick="@PixelStudio.SendToTileset">STAMP TILE</div>
</div>
}
else if ( GameFlow.Screen == GameScreen.Stage )
{
<div class="rail">
<div class="rail-title">STAMP</div>
@foreach ( var s in StageStudio.Stamps )
{
var stamp = s;
<div class="tool @(StageStudio.Stamp == stamp ? "on" : "")" @onclick="@(() => StageStudio.SelectStamp(stamp))">@stamp.ToUpper()</div>
}
<div class="row">
<div class="tool slim" @onclick="@(() => StageStudio.SetZoom(StageStudio.Zoom - 1))">−</div>
<div class="hint">@StageStudio.Zoom x</div>
<div class="tool slim" @onclick="@(() => StageStudio.SetZoom(StageStudio.Zoom + 1))">+</div>
</div>
<div class="tool slim @(StageStudio.Autotile ? "on" : "")" @onclick="@ToggleAutotile">AUTOTILE</div>
<div class="row">
<div class="tool slim" @onclick="@StageStudio.Undo">UNDO</div>
<div class="tool slim" @onclick="@StageStudio.Redo">REDO</div>
</div>
<div class="rail-title">GOAL</div>
<div class="row wrap">
<div class="tool slim @(Desk.Stage?.Goal != "grab" ? "on" : "")" @onclick="@(() => StageStudio.SetGoal("exit"))">EXIT</div>
<div class="tool slim @(Desk.Stage?.Goal == "grab" ? "on" : "")" @onclick="@(() => StageStudio.SetGoal("grab"))">GRAB</div>
</div>
<div class="rail-title">TILES</div>
<div class="tile-grid">
@if ( Desk.Tileset is not null )
{
@for ( var i = 1; i < Math.Min( Desk.Tileset.TileCount, 20 ); i++ )
{
var id = i;
var hex = Desk.Tileset.PreviewHex( id );
<div class="tile @(StageStudio.TileId == id && StageStudio.Stamp == "paint" ? "on" : "")" style="background-color: @hex" @onclick="@(() => StageStudio.SelectTile(id))">@id</div>
}
}
</div>
<div class="tool slim" @onclick="@StageStudio.CycleTileFlag">FLAG · @(Desk.Tileset is null ? "—" : TilesetDoc.FlagName( Desk.Tileset.Flag( StageStudio.TileId ) ))</div>
<div class="hint">WASD pan Wheel zoom MMB pan Back/Fwd undo</div>
</div>
}
else if ( GameFlow.Screen == GameScreen.Library )
{
<div class="rail wide">
<div class="rail-title">MY DESK</div>
<div class="tool" @onclick="@(() => LibraryStore.SaveCurrent(LibraryStore.TypeProject))">SAVE PROJECT</div>
<div class="tool" @onclick="@(() => LibraryStore.SaveCurrent(LibraryStore.TypeSprite))">SAVE SPRITE</div>
<div class="tool" @onclick="@(() => LibraryStore.SaveCurrent(LibraryStore.TypeTileset))">SAVE TILESET</div>
<div class="tool" @onclick="@(() => LibraryStore.SaveCurrent(LibraryStore.TypeStage))">SAVE STAGE</div>
<div class="tool primary" @onclick="@OnPublish">PUBLISH</div>
<div class="tool slim" @onclick="@LibraryStore.LoadStarter">STARTER PACK</div>
<div class="rail-title">SHELF</div>
@foreach ( var item in LibraryStore.Items )
{
var it = item;
<div class="lib-item" @onclick="@(() => LibraryStore.Open(it))" @onmouseover="@OnHover">
<div class="lib-title">@it.Title</div>
<div class="lib-meta">@it.Kind | @it.When</div>
</div>
}
</div>
}
else if ( GameFlow.Screen == GameScreen.World )
{
<div class="rail">
<div class="rail-title">STREET</div>
<div class="row">
<div class="tool slim" @onclick="@(() => WorldStudio.SetZoom(WorldStudio.Zoom - 1))">−</div>
<div class="hint">@WorldStudio.Zoom x</div>
<div class="tool slim" @onclick="@(() => WorldStudio.SetZoom(WorldStudio.Zoom + 1))">+</div>
</div>
<div class="tool primary" @onclick="@OnWalkStudio">WALK</div>
<div class="hint">Later. Clear Stock the Cooler, then Walk.</div>
</div>
}
else if ( GameFlow.Screen == GameScreen.Chip )
{
<div class="rail">
<div class="rail-title">TRACKER</div>
<div class="tool @(ChipStudio.Playing ? "on" : "")" @onclick="@ChipStudio.TogglePlay">@(ChipStudio.Playing ? "STOP" : "PLAY")</div>
<div class="row">
<div class="tool slim" @onclick="@(() => ChipStudio.SetBpm((Desk.Chip?.Bpm ?? 112) - 8))">−</div>
<div class="hint">@(Desk.Chip?.Bpm ?? 112) BPM</div>
<div class="tool slim" @onclick="@(() => ChipStudio.SetBpm((Desk.Chip?.Bpm ?? 112) + 8))">+</div>
</div>
<div class="tool slim" @onclick="@ChipStudio.Clear">CLEAR LOOP</div>
<div class="hint">Click a cell for a note. Right click changes pitch. Click the name to mute. Space plays.</div>
</div>
}
<div class="stage @(Lesson.Highlight == "canvas" ? "glow-stage" : "")">
<crtview></crtview>
@if ( GameFlow.Screen is GameScreen.Play2D or GameScreen.Play3D )
{
<div class="play-hud">
<div>COINS @PlaySim.Coins/@PlaySim.CoinTotal</div>
<div>@PlaySim.TimeText</div>
@if ( PlaySim.Walk3D )
{
<div>WALK</div>
}
@if ( PlaySim.Dead )
{
<div class="hurt">FALL</div>
}
</div>
@if ( !string.IsNullOrEmpty( PlaySim.SignText ) )
{
<div class="sign-read">@PlaySim.SignText</div>
}
}
</div>
@if ( GameFlow.Screen == GameScreen.Pixel )
{
<div class="rail right">
<div class="rail-title">PALETTE</div>
@foreach ( var pack in Palettes.Packs )
{
var p = pack;
<div class="tool slim @(Desk.Sprite is not null && Desk.Sprite.PaletteName == p.Name ? "on" : "")" @onclick="@(() => PixelStudio.SetPalette(p))">@p.Name</div>
}
<div class="swatches">
@if ( Desk.Sprite is not null )
{
@for ( var i = 0; i < Desk.Sprite.Palette.Length; i++ )
{
var idx = (byte)i;
var hex = Palettes.Hex( Desk.Sprite.Palette[i] );
var on = PixelStudio.Fg == idx ? "fg" : PixelStudio.Bg == idx ? "bg" : "";
<div class="swatch @on" style="background-color: @(hex == "transparent" ? "#11141d" : hex)" @onclick="@(() => PixelStudio.SelectColor(idx))"></div>
}
}
</div>
<div class="hint">[email protected] · [email protected]</div>
<div class="tool slim" @onclick="@PixelStudio.SwapFgBg">SWAP</div>
</div>
}
</div>
<div class="status">
<div class="coach">@Coach.Line</div>
<div class="meta">@Desk.Name | @(DeskNet.GuestLocked ? "watching" : (Desk.Dirty ? "draft" : "saved")) | Esc pause</div>
</div>
</div>
}
@if ( Lesson.Active )
{
<div class="lesson">
<div class="lesson-kicker">@Lesson.Title</div>
<div class="lesson-body">@Lesson.Body</div>
<div class="lesson-actions">
<div class="btn slim quiet" @onclick="@OnSkipTour" @onmouseover="@OnHover">SKIP TOUR</div>
</div>
</div>
}
@if ( !string.IsNullOrEmpty( GameFlow.Toast ) )
{
<div class="toast">@GameFlow.Toast</div>
}
</root>
@code
{
static readonly int[] Sizes = { 8, 16, 24, 32, 48, 64, 96, 128 };
protected override void OnStart()
{
Lesson.Boot();
LibraryStore.Refresh();
}
void OnHover() => ChipVoices.Hover();
void OnTour() => GameFlow.StartTour();
void OnSkipTour() => Lesson.Skip();
void OnBuild() => GameFlow.EnterBuild();
void OnPlay() => GameFlow.EnterPlay();
void OnWorld() => GameFlow.OpenStudio( GameScreen.World );
void OnWalk() => GameFlow.EnterWalk();
void OnWalkStudio() => GameFlow.StartWorldWalk();
void OnDesk()
{
Desk.Boot();
LibraryStore.Refresh();
GameFlow.OpenStudio( GameScreen.Library );
}
void OnTitle() => GameFlow.GoTitle();
void OnQuit() => GameFlow.Quit();
void OnResume() => GameFlow.Resume();
TextEntry _traceUrl;
void LoadTrace()
{
var typed = _traceUrl?.Text;
if ( !string.IsNullOrWhiteSpace( typed ) )
PixelRef.Load( typed );
else
PixelRef.Paste();
}
void OnAgain() => GameFlow.Replay();
void OnNextStage() => GameFlow.PlayNextSample();
void OnDropIn() => GameFlow.StartPlaytest();
void OnLeavePlay() => GameFlow.LeavePlay();
void OnSave() => LibraryStore.SaveCurrent( LibraryStore.TypeProject );
void OnPublish() => LibraryStore.PublishCurrent();
void Open( GameScreen s ) => GameFlow.OpenStudio( s );
void ToggleAutotile()
{
StageStudio.Autotile = !StageStudio.Autotile;
GameFlow.Bump();
}
protected override int BuildHash()
{
var a = HashCode.Combine(
GameFlow.Screen,
GameFlow.Ui,
GameFlow.Toast,
Coach.Line,
PixelStudio.Tool,
PixelStudio.Fg,
PixelStudio.Zoom,
HashCode.Combine( PixelStudio.Brush, PixelStudio.Role, PixelStudio.FillShape, PixelStudio.Circle, PixelStudio.Onion, PixelStudio.Playing, HashCode.Combine( PixelStudio.CursorX, PixelStudio.CursorY, PixelRef.Version, PixelStudio.Mirror, PixelStudio.HoverPx ) )
);
var b = HashCode.Combine(
StageStudio.TileId,
StageStudio.Stamp,
StageStudio.Zoom,
StageStudio.Autotile,
PlaySim.Coins,
PlaySim.Deaths,
PlaySim.Won,
Desk.Dirty
);
var c = HashCode.Combine(
Desk.Sprite?.Version ?? 0,
Desk.Tileset?.Version ?? 0,
Desk.Stage?.Version ?? 0,
LibraryStore.Version,
PlaySim.Version,
WorldStudio.Zoom,
PlaySim.SignText,
HashCode.Combine( Desk.Stage?.Goal, Lesson.Step, Lesson.Active, Lesson.Finished, ChipStudio.Head, HashCode.Combine( ChipStudio.Playing, Desk.Chip?.Version, DeskNet.Version, DeskNet.Watching, DeskNet.Presence ) )
);
return HashCode.Combine( a, b, c );
}
}