A Razor UI panel that displays a modal for rating a map with 1-5 stars. It shows the map title, handles mouse hover and controller navigation, submits ratings to GameStats, and manages a pending rating identifier from the lobby flow.
@using Sandbox;
@using Sandbox.UI;
@using Machines.Components;
@using Machines.Resources;
@using Machines.Systems;
@namespace Machines.UI
@inherits Panel
@{
var map = ResolveMap();
var title = map?.Title ?? "this map";
}
@if ( _ident != null )
{
<root class="rating-modal @(Input.UsingController ? "controller" : "mouse")">
<div class="backdrop"></div>
<div class="card">
<div class="eyebrow">RATE THE MAP</div>
<div class="question">Did you enjoy @title?</div>
<div class="stars" onmouseout=@(() => _hover = 0)>
@for ( int i = 1; i <= 5; i++ )
{
var star = i;
var filled = star <= (_hover > 0 ? _hover : _cursor);
<div class="star @(filled ? "on" : "")" onmouseover=@(() => _hover = star) onclick=@(() => Submit( star ))>
<i>@(filled ? "star" : "star_outline")</i>
</div>
}
</div>
<div class="dismiss" onclick=@Dismiss>Maybe later</div>
</div>
</root>
}
else
{
<root class="rating-modal">
</root>
}
@code
{
// Dev: open the rating modal for the map currently selected in the menu.
[ConCmd( "rate_map" )]
public static void OpenForSelectedMap()
{
var flow = LobbyFlow.Current;
var ident = flow?.LocalBrowsedMapIdent ?? flow?.SelectedMapIdent;
if ( string.IsNullOrEmpty( ident ) )
{
Log.Warning( "rate_map: no map selected." );
return;
}
GameStats.PendingRatingMapIdent = ident;
}
// Map currently being rated; null when the modal is closed.
private string _ident;
// Hovered star (mouse) and controller cursor position, 1-5.
private int _hover;
private int _cursor = 3;
private MapResource ResolveMap()
{
if ( string.IsNullOrEmpty( _ident ) )
return null;
return ResourceLibrary.Get<MapResource>( _ident );
}
public override void Tick()
{
base.Tick();
// Adopt a pending rating once we're back in the lobby.
if ( _ident == null && !string.IsNullOrEmpty( GameStats.PendingRatingMapIdent ) )
{
_ident = GameStats.PendingRatingMapIdent;
_hover = 0;
_cursor = 3;
}
if ( _ident == null || !Input.UsingController )
return;
// Controller navigation; clear actions so they don't bleed into the lobby.
if ( Input.Pressed( "MenuLeft" ) ) { Input.Clear( "MenuLeft" ); _cursor = System.Math.Max( 1, _cursor - 1 ); }
if ( Input.Pressed( "MenuRight" ) ) { Input.Clear( "MenuRight" ); _cursor = System.Math.Min( 5, _cursor + 1 ); }
if ( Input.Pressed( "MenuSelect" ) ) { Input.Clear( "MenuSelect" ); Submit( _cursor ); }
if ( Input.Pressed( "MenuBack" ) ) { Input.Clear( "MenuBack" ); Dismiss(); }
}
private void Submit( int stars )
{
GameStats.SetMapRating( _ident, stars );
Sound.Play( "button_accept" );
Close();
}
private void Dismiss() => Close();
private void Close()
{
_ident = null;
_hover = 0;
GameStats.PendingRatingMapIdent = null;
}
protected override int BuildHash() => System.HashCode.Combine( _ident, _hover, _cursor, Input.UsingController );
}