Game/2D/UI/BombWheel.razor

A Razor UI component for the BombWheel HUD in a 2D Chain Reaction game. It renders the player's bomb slots, selection badges, jam overlay, a detonate button, handles keyboard slot shortcuts, listens for hand and game state changes, and updates a screen-shake CSS class.

NetworkingFile Access
@using Sandbox;
@using Sandbox.UI;
@using System;
@inherits PanelComponent

<root>
	  @* Screen Shake the whole root*@
		@if ( Hand != null && Game?.IsRunning == true )
		{
			<div class="mini-hand @_currentShake">
				@for ( int i = 0; i < Hand.Hand.Count; i++ )
				{
					int  slot   = i;
					bool placed = Hand.IsSlotPlaced(slot);
					bool sel    = Hand.SelectedSlot == slot && !placed;
					var  bomb   = Hand.BombAt(slot);
					
					<div class="mini-card @(placed?"used":sel?"active":"")"
						onclick=@(()=>Hand.SelectSlot(slot))>

						@* Keyboard hint badge top-left *@
						@if(!placed)
						{
							<div class="kb-hint">@(slot + 1)</div>
						}
						<div class="mini-icon">@Icon(bomb)</div>
						<div class="mini-name">@ShortName(bomb)</div>

						@* Jammed slot overlay *@
						@if (Hand?.IsSlotJammed(slot) == true)
						{
							<div class="mini-jam-overlay">🔒</div>
						}
						@if(placed)
						{ 
							int n = GetNum(slot); 
							<div class="mini-badge">@n</div> 
						}
					</div>
				}
				@* Detonate button *@
				<div class="mini-det-wrap @(CanDet?"ready":"off")" onclick=@OnDet>
					<div class="mini-det">
						<div class="mini-det-icon">💥</div>
						<div class="mini-det-label">SPACE</div>
					</div>
				</div>
			</div>
		}
</root>

@code {
	BombHandSystem    Hand => ChainReactionGame.Instance?.Components.Get<BombHandSystem>();
	ChainReactionGame Game => ChainReactionGame.Instance;

	bool CanDet => Game?.IsRunning == true && Hand?.AnyPlaced == true && !(Hand?.IsDetonating ?? true);

	bool _sub = false;

	protected override void OnTreeFirstBuilt() => TrySub();

	protected override void OnUpdate()
	{
		if (!_sub) TrySub();

		// Keyboard shortcuts: 1/2/3 to select bomb slots
		if (Game?.IsRunning == true && !(Hand?.IsDetonating ?? true))
		{
			if (ChainReactionGame.Instance?.IsBombJamActive == true)
			{
				// Allow all slots EXCEPT the jammed one
				for (int s = 0; s < (Hand?.HandSize ?? 0); s++)
				{
					if (Hand?.IsSlotJammed(s) == true) continue;
					if (s == 0 && Input.Pressed("slot1")) Hand?.SelectSlot(0);
					if (s == 1 && Input.Pressed("slot2")) Hand?.SelectSlot(1);
					if (s == 2 && Input.Pressed("slot3")) Hand?.SelectSlot(2);
					if (s == 3 && Input.Pressed("Slot4")) Hand?.SelectSlot(3);
				}
			}
			else
			{
				if(Input.Pressed("slot1")) Hand?.SelectSlot(0);
				if(Input.Pressed("slot2")) Hand?.SelectSlot(1);
				if(Input.Pressed("slot3")) Hand?.SelectSlot(2);
				if(Input.Pressed("Slot4")) Hand?.SelectSlot(3);
				//if(Input.Pressed("Slot5")) Hand?.SelectSlot(4);
			}
		}

		  // Screen Shake
        var shakeClass = ScreenShaker.Instance?.ShakeClass ?? "";
        if (shakeClass != _currentShake)
        {
            _currentShake = shakeClass;
            StateHasChanged();
        }
	}
	string _currentShake = "";

	void OnDet()
	{
		if (!CanDet) return;

		// Fire tutorial advance before detonating
    	TutorialOverlay.Instance?.TryAdvance(TutorialOverlay.TutorialTrigger.Detonated);

		Game?.Detonate();
	}

	void TrySub()
	{
		var h = Hand; var g = Game;
		if (h is null || g is null) return;
		if (_sub) return;
		h.OnHandChanged  += StateHasChanged;
		g.OnStateChanged += StateHasChanged;
		_sub = true;
		StateHasChanged();
	}

	int GetNum(int slot)
	{
		var o = Hand?.PlacementOrder;
		if (o is null) return 0;
		int idx = o.IndexOf(slot);
		return idx >= 0 ? idx + 1 : 0;
	}

	protected override int BuildHash() => System.HashCode.Combine(
		Hand?.SelectedSlot, Hand?.PlacedSlots.Count, Hand?.HasSwapped, Hand?.Hand.Count, CanDet, _currentShake);

	static string Icon(GridManager.BombType t) => t switch {
		GridManager.BombType.Cross    => "✛", GridManager.BombType.Sniper   => "↑",
		GridManager.BombType.Diagonal => "✕", GridManager.BombType.Square   => "■",
		GridManager.BombType.Chain    => "⛓", _ => "?" };

	static string ShortName(GridManager.BombType t) => t switch {
		GridManager.BombType.Cross    => "Cross",  GridManager.BombType.Sniper   => "Sniper",
		GridManager.BombType.Diagonal => "Diag",   GridManager.BombType.Square   => "Square",
		GridManager.BombType.Chain    => "Chain",  _ => "?" };
}