MainMenuUi.razor
@using Sandbox;
@using Sandbox.UI;
@using System.Linq;
@inherits PanelComponent
<root>
<div class="bg">
<!-- Blob 1: Cyan - 5 children (рухливі клякси) -->
<div class="blob-group b1">
<div class="c c1a"></div>
<div class="c c1b"></div>
<div class="c c1c"></div>
<div class="c c1d"></div>
<div class="c c1e"></div>
</div>
<!-- Blob 2: Purple - 4 children (рухливі клякси) -->
<div class="blob-group b2">
<div class="c c2a"></div>
<div class="c c2b"></div>
<div class="c c2c"></div>
<div class="c c2d"></div>
</div>
<!-- Blob 3: Green - 3 children (рухливі клякси) -->
<div class="blob-group b3">
<div class="c c3a"></div>
<div class="c c3b"></div>
<div class="c c3c"></div>
</div>
<!-- Blob 4: Pink - 4 children (рухливі клякси) -->
<div class="blob-group b4">
<div class="c c4a"></div>
<div class="c c4b"></div>
<div class="c c4c"></div>
<div class="c c4d"></div>
</div>
<!-- Blob 5: Deep Blue - 2 children (рухливі клякси) -->
<div class="blob-group b5">
<div class="c c5a"></div>
<div class="c c5b"></div>
</div>
<div class="noise"></div>
</div>
<div class="layout">
<div class="left-panel">
<div class="logo">PAINT<span>SLIME</span></div>
<div class="tagline">3v3 Multiplayer Paint Arena</div>
</div>
<div class="right-panel">
<div class="btn btn-multi" onclick=@ShowMultiplayer>Multiplayer</div>
<div class="btn btn-exit" onclick=@QuitGame>Quit</div>
</div>
</div>
<!-- Matchmaking / Lobby Overlay -->
@if ( Matchmaking.IsValid() && Matchmaking.IsSearching )
{
<div class="lobby-overlay">
<div class="lobby-card">
<div class="spinner-container">
<div class="spinner-ring"></div>
<div class="spinner-core"></div>
</div>
<h2>MATCHMAKING</h2>
<div class="status-msg">@Matchmaking.StatusMessage</div>
<div class="player-slots">
@for ( int i = 0; i < Matchmaking.RequiredPlayers; i++ )
{
var slotActive = i < Matchmaking.ConnectedPlayers;
var slotReady = i < Matchmaking.ReadyPlayers.Count;
<div class="slot @(slotActive ? "active" : "") @(slotReady ? "ready" : "")">
<div class="slot-dot"></div>
</div>
}
</div>
<div class="lobby-actions">
@{
bool isLobbyFull = Matchmaking.ConnectedPlayers >= Matchmaking.RequiredPlayers;
bool amIReady = Connection.Local != null && Matchmaking.ReadyPlayers.Contains( Connection.Local.Id );
}
<div class="btn-lobby btn-ready @(isLobbyFull ? "active" : "disabled") @(amIReady ? "is-ready" : "")" onclick=@ToggleReadyStatus>
@(amIReady ? "READY!" : "START")
</div>
<div class="btn-lobby btn-cancel" onclick=@CancelSearch>Cancel</div>
</div>
</div>
</div>
}
</root>
@code {
[Property] public MatchmakingManager Matchmaking { get; set; }
protected override void OnStart()
{
// Шукаємо компонент на сцені, якщо не призначено вручну
if ( !Matchmaking.IsValid() )
{
Matchmaking = Scene.GetAllComponents<MatchmakingManager>().FirstOrDefault();
}
// Автоматично створюємо MatchmakingManager, якщо його немає на сцені,
// щоб пошук працював одразу «з коробки» без додаткових ручних налаштувань
if ( !Matchmaking.IsValid() )
{
Matchmaking = GameObject.Components.Create<MatchmakingManager>();
}
}
void ShowMultiplayer()
{
if ( Matchmaking.IsValid() )
{
Matchmaking.StartMatchmaking();
}
else
{
Log.Error( "MatchmakingManager not found in the scene! Try adding it manually." );
}
}
void CancelSearch()
{
if ( Matchmaking.IsValid() )
{
Matchmaking.CancelMatchmaking();
}
}
void ToggleReadyStatus()
{
if ( !Matchmaking.IsValid() || Matchmaking.ConnectedPlayers < Matchmaking.RequiredPlayers ) return;
if ( Connection.Local == null ) return;
Matchmaking.ToggleReady( Connection.Local.Id );
}
void QuitGame() { Game.Close(); }
protected override int BuildHash()
{
var isSearching = Matchmaking.IsValid() && Matchmaking.IsSearching;
var players = Matchmaking.IsValid() ? Matchmaking.ConnectedPlayers : 0;
var ready = Matchmaking.IsValid() ? Matchmaking.ReadyPlayers.Count : 0;
var msg = Matchmaking.IsValid() ? Matchmaking.StatusMessage : "";
var isHost = Sandbox.Networking.IsHost;
return System.HashCode.Combine( isSearching, players, msg, isHost, ready );
}
}