Update default pause screen (#3659)

This commit is contained in:
Garry Newman
2025-12-22 18:13:07 +00:00
committed by GitHub
parent 39d80352c7
commit e0ce9ace1d
8 changed files with 536 additions and 199 deletions

View File

@@ -1,4 +1,5 @@

using Microsoft.AspNetCore.Components;
using Sandbox.Engine;
namespace Sandbox.UI;
@@ -8,11 +9,13 @@ public partial class Panel
/// <summary>
/// A string to show when hovering over this panel.
/// </summary>
[Parameter]
public string Tooltip { get; set; }
/// <summary>
/// The created tooltip element will have this class, if set.
/// </summary>
[Parameter]
public string TooltipClass { get; set; }
/// <summary>

View File

@@ -1,4 +1,5 @@
using MenuProject.Modals;
using MenuProject.Modals.PauseMenuModal;
using Sandbox;
using Sandbox.Modals;
using Sandbox.Services;
@@ -166,7 +167,7 @@ public class ModalSystem : IModalSystem
return;
}
var modal = new PauseMenuModal();
var modal = new PauseModal();
Push( modal );
}
@@ -190,5 +191,5 @@ public class ModalSystem : IModalSystem
}
public bool IsModalOpen => OpenModals.Any();
public bool IsPauseMenuOpen => OpenModals.OfType<PauseMenuModal>().Any();
public bool IsPauseMenuOpen => OpenModals.OfType<PauseModal>().Any();
}

View File

@@ -1,100 +0,0 @@
@using Sandbox.Modals
@using Sandbox
@using Sandbox.UI
@using Sandbox.Menu
@using MenuProject.MenuUI.Components
@namespace MenuProject.Modals
@inherits MenuProject.Modals.BaseModal
<root>
<div class="inner">
@if(game is not null)
{
<MediaCardWide Image="@game.ThumbWide" OnPressImage="@( () => game.OpenModal() )">
<Content>
<div class="title clicky" onmousedown="@( ()=> game.OpenModal() )">@game.Title</div>
<div class="subtitle">@game.Summary</div>
<div class="meta">
<Meta Title="Likes" Icon="thumb_up" Value="@game.VotesUp.KiloFormat()" />
<Meta Title="Players" Icon="people" Value="@game.Usage.Total.Users.KiloFormat()" />
</div>
</Content>
</MediaCardWide>
}
<div class="body">
<div class="options">
<Button Icon="play_arrow" @onclick="@(() => this.Delete())">Resume</Button>
@if ( game is not null )
{
<Button Icon="info" @onclick="@(() => game.OpenModal() )">About</Button>
@if ( Networking.IsActive && game.GetValue<bool>( "ShowPlayerList", true ) )
{
<Button Icon="groups" @onclick="@(() => Game.Overlay.ShowPlayerList())">Players</Button>
}
@if ( CanChangeMap )
{
<Button Icon="map" onclick=@ChangeMap>Change Map</Button>
}
}
<Button Icon="gamepad" @onclick="@(() => Game.Overlay.ShowBinds() )">Controls</Button>
<Button Icon="settings" @onclick="@(() => Game.Overlay.ShowSettingsModal() )">Settings</Button>
<Button Icon="close" @onclick="@(() => MenuUtility.CloseGame())">Leave Game</Button>
</div>
</div>
</div>
</root>
@code
{
bool CanChangeMap
{
get
{
if ( Networking.IsActive && !Networking.IsHost ) return false;
return game.GetValue( "ShowChangeMap", false );
}
}
Package game;
void OnCreateGame( CreateGameResults x )
{
if ( Networking.IsActive && !Networking.IsHost )
return;
if ( !string.IsNullOrEmpty( x.MapIdent ) )
{
LaunchArguments.Map = x.MapIdent;
}
Game.Load( Game.Ident, true );
this.CloseModal( true );
}
void ChangeMap()
{
Game.Overlay.CreateGame( new CreateGameOptions( game, OnCreateGame ) );
}
public PauseMenuModal() : base()
{
game = MenuUtility.GamePackage;
}
}

View File

@@ -1,97 +0,0 @@
PauseMenuModal
{
font-family: Poppins;
pointer-events: all;
font-size: 11px;
> .inner
{
flex-direction: column;
background-color: transparent;
justify-content: center;
align-items: center;
}
}
.options
{
flex-direction: column;
font-family: Poppins;
font-size: 24px;
font-weight: bold;
}
button.button
{
opacity: 0.5;
gap: 16px;
transition: all 0.1s ease;
width: 300px;
}
button:hover
{
opacity: 1;
gap: 24px;
}
.options .iconpanel
{
font-size: 32px;
color: #3272ea;
}
.subtitle, .title
{
flex-shrink: 0;
font-weight: bold;
font-size: 40px;
}
.subtitle
{
font-size: 18px;
font-weight: normal;
margin-bottom: 32px;
margin-top: -5px;
opacity: 0.3;
}
MediaCardWide
{
width: 800px;
margin-bottom: 100px;
pointer-events: none;
}
MediaCardWide > .inner > .image > .inner
{
width: 300px;
aspect-ratio: 16/9;
pointer-events: all;
}
MediaCardWide .content .title
{
font-size: 32px;
pointer-events: all;
cursor: pointer;
}
Meta > .inner
{
pointer-events: all;
color: #747e90;
i
{
color: #636a77
}
}
.meta
{
padding: 8px;
gap: 16px;
}

View File

@@ -0,0 +1,113 @@
@using Sandbox.Modals
@using Sandbox
@using Sandbox.UI
@using Sandbox.Menu
@using MenuProject.MenuUI.Components
@inherits Panel
<root>
@{
var icon = Package.ThumbWide ?? Package.Thumb;
@if (icon != null )
{
<div class="thumb clicky" style="background-image: url( @icon )" @onclick=@(() => Package.OpenModal())>
<div class="light-border"></div>
</div>
}
}
<div class="contents">
<div class="title-org">
<div class="title clicky" onmousedown="@( ()=> Package.OpenModal() )">@Package.Title</div>
<div class="org clicky" onmousedown="@( ()=> Game.Overlay.ShowOrganizationModal( Package.Org ) )">by @Package.Org?.Title</div>
</div>
@if ( !string.IsNullOrWhiteSpace( Package.Summary ) )
{
<div class="summary">@Package.Summary</div>
}
<div class="metrics">
<Button Icon="directions_run" Disabled Text="@(Package.Usage.Total.Users.ToMetric( 0 ) + " users")"></Button>
<Button Icon="play_arrow" Disabled Text="@(Package.Usage.Total.Sessions.ToMetric( 0 ) + " plays")"></Button>
<Button Icon="favorite" Active="@IsFavourite" class="favorite" Text="@(Package.Favourited.ToMetric() + " favorites")" onclick="@ToggleFavourite"></Button>
<Button Icon="bug_report" Disabled Text="@((Package.ErrorRate * 100.0f).Clamp( 0, 100).ToString( "0\\%" ) + " errors")"></Button>
<Button Icon="thumb_up" Active="@(Package.Interaction.Rating == 0)" Text="@(Package.VotesUp.ToMetric() + " likes")" onclick="@VoteUp"></Button>
<Button Icon="thumb_down" Active="@(Package.Interaction.Rating == 1)" Text="@(Package.VotesDown.ToMetric() + " dislikes" )" onclick="@VoteDown"></Button>
<Button Icon="reviews"
Text="@($"{Package.Reviews.Score.ToString( "N0" )}% from {Package.Reviews.Total} Reviews")"
onclick="@( () => Game.Overlay.ShowReviewModal(Package) )"></Button>
</div>
@if ( Package.Interaction.Used )
{
<div class="statsbox">
<div class="row">
<div class="key">Play Time</div>
<div class="value">@TimeSpan.FromSeconds( Package.Interaction.Seconds ).Humanize( 1 )</div>
</div>
<div class="row">
<div class="key">Sessions</div>
<div class="value">@Package.Interaction.Sessions.ToMetric( 0 )</div>
</div>
<div class="row">
<div class="key">First Played</div>
<div class="value">@Package.Interaction.FirstUsed?.Date.Humanize()</div>
</div>
@if ( ach != null )
{
var achText = $"{ach.All.Where(x => x.IsUnlocked).Count().ToMetric()} of {ach.All.Count().ToMetric()}";
<div class="row">
<div class="key">Achievements</div>
<div class="value">@achText</div>
</div>
}
</div>
}
</div>
</root>
@code
{
[Parameter]
public Package Package { get; set; }
AchievementCollection ach;
protected override async Task OnParametersSetAsync()
{
ach = await Package.GetAchievements();
}
bool IsFavourite => Package.Interaction.Favourite;
async void VoteUp()
{
await Package.SetVoteAsync(true);
StateHasChanged();
}
async void VoteDown()
{
await Package.SetVoteAsync(false);
StateHasChanged();
}
async void ToggleFavourite()
{
await Package.SetFavouriteAsync(!IsFavourite);
StateHasChanged();
}
}

View File

@@ -0,0 +1,152 @@
GameInfoBox
{
width: 100%;
flex-direction: column;
font-size: 1.2rem;
> .thumb
{
width: 100%;
aspect-ratio: 16/9;
background-color: red;
border-radius: 10px;
background-size: cover;
background-position: center;
position: relative;
.light-border
{
position: absolute;
width: 100%;
height: 100%;
border: 1px solid #fff1;
border-radius: 10px;
}
&:hover
{
.light-border
{
border: 1px solid #fff4;
}
}
}
}
.contents
{
flex-direction: column;
width: 100%;
.summary
{
justify-content: center;
}
}
.title-org
{
font-weight: 700;
width: 100%;
padding: 1rem;
align-items: center;
justify-content: space-between;
.title
{
font-size: 2rem;
}
.org
{
font-size: 1rem;
}
.title, .org
{
padding: 0.5rem 1rem;
border-radius: 5px;
&:hover
{
background-color: #fff2;
}
}
}
.statsbox
{
flex-direction: column;
padding: 1rem 2rem;
background-color: #0004;
border-radius: 10px;
margin-top: 2rem;
h1
{
font-weight: 300;
font-size: 1.5rem;
margin-bottom: 10px;
text-transform: uppercase;
color: #08f;
}
.row
{
.key
{
width: 50%;
opacity: 0.6;
}
.value
{
font-weight: bold;
}
}
}
.metrics
{
margin-top: 2rem;
flex-wrap: wrap;
gap: 0.25rem;
.button
{
background-color: #0004;
width: 40%;
flex-grow: 1;
border-radius: 8px;
opacity: 1;
&:hover
{
background-color: #0006;
}
&:active
{
background-color: #0002;
}
&.active
{
color: #08f;
}
&.disabled
{
pointer-events: none;
}
&.favorite
{
&.active
{
color: #ff0058;
}
}
}
}

View File

@@ -0,0 +1,111 @@
@using Sandbox.Modals
@using Sandbox
@using Sandbox.UI
@using Sandbox.Menu
@using MenuProject.MenuUI.Components
@inherits MenuProject.Modals.BaseModal
<root>
<div class="inner">
<div class="tiles-container">
<div class="left">
@if (game is not null)
{
<GameInfoBox Package="@game"></GameInfoBox>
}
</div>
<div class="right">
</div>
</div>
<div class="bottom-bar">
<div class="left">
<Button class="exit" Tooltip="Return to s&box main menu" Icon="west" @onclick="@(() => MenuUtility.CloseGame())" Text="Leave"></Button>
</div>
<div class="center">
@if (CanChangeMap)
{
<Button class="circle" Tooltip="Change Map" Icon="map" onclick=@ChangeMap></Button>
}
@if (Networking.IsActive && game.GetValue<bool>("ShowPlayerList", true))
{
<Button class="circle" Icon="groups" Tooltip="Player List" @onclick="@(() => Game.Overlay.ShowPlayerList())"></Button>
}
<Button class="circle" Tooltip="Reconfigure Controls" Icon="gamepad" @onclick="@(() => Game.Overlay.ShowBinds())"></Button>
<Button class="circle" Tooltip="Open Settings" Icon="settings" @onclick="@(() => Game.Overlay.ShowSettingsModal())"></Button>
</div>
<div class="right">
<Button class="resume" Icon="play_arrow" @onclick="@(() => this.Delete())" Text="Resume"></Button>
</div>
</div>
</div>
</root>
@code
{
bool CanChangeMap
{
get
{
if ( Networking.IsActive && !Networking.IsHost ) return false;
return game.GetValue( "ShowChangeMap", false );
}
}
Package game;
void OnCreateGame( CreateGameResults x )
{
if ( Networking.IsActive && !Networking.IsHost )
return;
if ( !string.IsNullOrEmpty( x.MapIdent ) )
{
LaunchArguments.Map = x.MapIdent;
}
Game.Load( Game.Ident, true );
this.CloseModal( true );
}
void ChangeMap()
{
Game.Overlay.CreateGame( new CreateGameOptions( game, OnCreateGame ) );
}
public PauseModal() : base()
{
game = MenuUtility.GamePackage;
}
protected override async Task OnParametersSetAsync()
{
if ( !game.IsRemote )
{
var ident = game.FullIdent.Split('#')[0];
game = await Package.FetchAsync(ident, false) ?? game;
StateHasChanged();
}
}
}

View File

@@ -0,0 +1,154 @@
PauseModal.modal
{
font-family: Poppins;
pointer-events: all;
font-size: 11px;
position: absolute;
> .inner
{
flex-direction: column;
background-color: transparent;
justify-content: center;
align-items: center;
width: 1920px;
height: 1280px;
position: relative;
padding: 4rem;
gap: 2rem;
}
}
PauseModal.modal > .inner > .bottom-bar
{
max-width: 1800px;
width: 100%;
gap: 2rem;
background-color: #111a;
backdrop-filter: blur( 20px );
padding: 2rem;
border-radius: 24px;
.left
{
}
.center
{
flex-grow: 1;
justify-content: center;
gap: 1rem;
}
.right
{
gap: 1rem;
}
button.button
{
width: auto;
font-size: 2rem;
font-weight: 700;
background-color: #fff3;
border-radius: 10px;
padding: 2rem 3rem;
opacity: 1;
.icon
{
font-size: 3rem;
}
&.circle
{
border-radius: 100px;
aspect-ratio: 1;
padding: 1rem;
align-items: center;
justify-content: center;
background-color: #575e6b;
color: #b5bdd6;
&:hover
{
background-color: #9dcc00;
color: #fff;
}
.icon
{
font-size: 3rem;
}
}
&.exit
{
background-color: #99004a;
color: #fffc;
text-shadow: 0px 0px 2px #0005;
&:hover
{
background-color: #cc0063;
color: #fff;
}
}
&.resume
{
flex-direction: row-reverse;
background-color: #006ccc;
color: #fffc;
text-shadow: 0px 0px 2px #0005;
&:hover
{
background-color: #08f;
color: #fff;
}
}
}
}
.options
{
flex-direction: column;
font-size: 24px;
font-weight: bold;
}
button.button
{
opacity: 0.5;
gap: 16px;
width: 300px;
}
button:hover
{
opacity: 1;
}
PauseModal .tiles-container
{
flex-grow: 1;
width: 100%;
gap: 2rem;
& > .left
{
width: 450px;
overflow: hidden;
}
& > .right
{
flex-grow: 1;
overflow: hidden;
}
}