Razor UI component for the game's HUD. It composes many child UI panels (spectator list, info, perks, timers, menus, tooltips) and controls cursor visibility, perk/shop/tooltips placement, dash reminder logic, and a custom build-hash to decide when to re-render.
@namespace Sandbox
@using Sandbox;
@using Sandbox.UI;
@using System;
@inherits PanelComponent
@{
var manager = Manager.Instance;
var player = manager.LocalPlayer;
var infoPlayer = manager.IsSpectator && manager.SelectedPlayer.IsValid()
? manager.SelectedPlayer
: player;
// Show the system cursor in any interactive or spectator state.
var showCursor =
manager.GameState == GameState.Lobby ||
manager.IsSpectator ||
manager.IsPaused ||
manager.IsHoveringPerkChoicePanel ||
manager.IsHoveringStatsTab ||
manager.IsEscMenuOpen ||
manager.IsOptionsMenuOpen ||
manager.ShouldShowGameOverScreen ||
manager.ShouldShowPerkUnlockProgressPanel ||
manager.ShouldShowQuestProgressPanel ||
manager.HoveredPerkType != null;
}
<root class="@(Input.UsingController ? "" : "cursor")">
<SpectatorListPanel />
@if( manager.GameState == GameState.Lobby )
{
<LobbyMenu />
@if (manager.IsOptionsMenuOpen)
{
<OptionsMenu />
}
}
else
{
@if ( manager.IsSpectator )
{
<SpectatorPanel />
}
if(infoPlayer.IsValid())
{
<InfoPanel Player=@infoPlayer />
@if ( manager.DashReminderActive )
{
var sp = manager.DashReminderScreenPos;
var spRel = sp / Screen.Size;
var panelStyle = $"position: absolute; left: {Length.Fraction(spRel.x)}; top: {Length.Fraction(spRel.y + 40f / Screen.Height)};";
<DashReminderPanel OnComplete=@(() => {
manager.DashReminderActive = false;
}) style=@panelStyle />
}
@if ( infoPlayer.IsValid() )
{
<BanishedPerks />
}
@if(player.IsValid() && player.ShouldShowStats)
{
<StatsScreen/>
}
else if ( player.IsValid() )
{
<StatsTab/>
}
}
<TimerPanel />
<PlayerList />
<PlayerPerks />
@if (manager.IsPaused)
{
<PausedOverlay />
}
else if (!manager.IsGameOver && !manager.IsEscMenuOpen)
{
// hack to make perk choice panel refresh size when onlyShowIcons changing value
if(!(Manager.Instance.SkipShowingChoicesFrames > 0))
{
<PerkChoicePanel />
}
}
@if (manager.ShouldShowPerkUnlockProgressPanel || manager.ShouldShowQuestProgressPanel || manager.ShouldShowGameOverScreen)
{
<div style="position: absolute; top: 12px; left: 16px; flex-direction: row; align-items: center; gap: 6px; pointer-events: none; z-index: 10;">
<div style="width: 28px; height: 28px; background-image: url(/textures/ui/coin.png); background-size: contain; background-repeat: no-repeat; background-position: center;"></div>
<label style="font-size: 24px; font-family: Cal Sans; color: rgba(255, 220, 80, 0.95); text-shadow: 2px 2px 0px black;">@ProgressManager.GetCoins()</label>
</div>
}
@if (manager.ShouldShowPerkUnlockProgressPanel)
{
<PerkUnlockProgressPanel />
}
else if (manager.ShouldShowQuestProgressPanel)
{
<QuestPanel RunProgressMode=@true />
}
else if (manager.ShouldShowGameOverScreen)
{
<GameOverScreen />
}
else if (manager.IsOptionsMenuOpen)
{
<OptionsMenu />
}
else if( manager.IsEscMenuOpen )
{
<EscMenu />
}
@if (manager.ShowBossHealthbar)
{
<BossNametag [email protected] />
}
@if( Game.IsEditor )
{
@* <DebugPanel />
@if(manager.ShowAllPerks)
{
<DebugAllPerksPanel />
} *@
}
}
@if(manager.HoveredPerkType != null)
{
var isChoice = manager.IsHoveredPerkAChoice;
var hoveredChoicePlayer = manager.HoveredPerkViewedPlayer;
var hoveredChoiceSlot = isChoice ? manager.HoveredPerkChoiceSlot : -1;
var hoveredChoiceUnknown = isChoice && hoveredChoicePlayer.IsValid() && hoveredChoiceSlot >= 0
? hoveredChoicePlayer.GetDisplayedPerkChoiceUnknown( hoveredChoiceSlot )
: false;
var tooltipOffsetScale = Math.Clamp( Math.Min( Screen.Width / 1920f, Screen.Height / 1080f ), 0.45f, 1f );
var choiceMouseOffset = new Vector2( 10f, -160f ) * tooltipOffsetScale;
var tooltipMousePadding = new Vector2( 20f, 20f ) * tooltipOffsetScale;
var mousePos = Mouse.Position;
if(isChoice)
mousePos += choiceMouseOffset;
var mousePosRelative = mousePos / Screen.Size;
string tooltipStyle = "position: absolute; pointer-events: none; transition: all 0s; z-index: 9999;";
if(mousePos.x > Screen.Width * 0.75f && !manager.ShowPerkUnlockPanel)
{
tooltipStyle += $" right:{Length.Fraction(1f - mousePosRelative.x + tooltipMousePadding.x / Screen.Width)};";
}
else
{
tooltipStyle += $" left:{Length.Fraction(mousePosRelative.x + tooltipMousePadding.x / Screen.Width)};";
}
tooltipStyle += $" top: {Length.Fraction(mousePosRelative.y + tooltipMousePadding.y / Screen.Height)};";
List<string> perkInfoRows = null;
if ( manager.ShowPerkUnlockPanel )
{
var hoveredAttrib = manager.HoveredPerkType.GetAttribute<PerkAttribute>();
perkInfoRows = new List<string>();
if ( hoveredAttrib.MultiplayerMode == MultiplayerMode.OnlyMultiplayer )
perkInfoRows.Add( "Multiplayer only" );
if ( hoveredAttrib.OnlyUnpausedChoosing )
perkInfoRows.Add( "Unpaused choosing only" );
if ( perkInfoRows.Count == 0 ) perkInfoRows = null;
}
var showUpgradeDescription = manager.ShowPerkUnlockPanel || (!isChoice && manager.HoveredPerkPanel == null);
<PerkChoice style=@(tooltipStyle) ViewedPlayer=@hoveredChoicePlayer [email protected] [email protected] IsChoice=@isChoice Slot=@hoveredChoiceSlot IsUnknown=@hoveredChoiceUnknown InfoRows=@perkInfoRows ShowUpgradeDescription=@showUpgradeDescription />
}
@if ( manager.HoveredShopItem.HasValue )
{
var mousePos = Mouse.Position;
var mousePosRelative = mousePos / Screen.Size;
string itemTooltipStyle = "position: absolute; pointer-events: none; transition: all 0s; z-index: 9999;";
if ( mousePos.x > Screen.Width * 0.75f )
itemTooltipStyle += $" right:{Length.Fraction(1f - mousePosRelative.x + 20f / Screen.Width)};";
else
itemTooltipStyle += $" left:{Length.Fraction(mousePosRelative.x + 20f / Screen.Width)};";
itemTooltipStyle += $" top: {Length.Fraction(mousePosRelative.y + 20f / Screen.Height)};";
<LoadoutItemPanel style=@(itemTooltipStyle) [email protected] IsShopMode=@false />
}
else if(manager.GameState == GameState.Playing)
{
if(manager.HoveredPlayer.IsValid())
{
<PlayerTooltip [email protected] ShowIcon=@true />
}
else if(manager.HoveredPlayerIcon.IsValid())
{
<PlayerTooltip [email protected] ShowIcon=@false />
}
}
</root>
@code
{
protected override int BuildHash()
{
var manager = Manager.Instance;
var player = manager.LocalPlayer;
var playerHash = player.IsValid() ? player.ShouldShowStats : false;
var isChoosing = player.IsValid() && player.IsChoosingLevelUpReward;
var menuHash = System.HashCode.Combine(
manager.IsPaused,
manager.IsEscMenuOpen,
manager.IsOptionsMenuOpen,
manager.ShouldShowGameOverScreen,
manager.ShouldShowQuestProgressPanel,
manager.ShouldShowPerkUnlockProgressPanel
);
var hash1 = System.HashCode.Combine(
menuHash,
manager.GameState,
manager.IsGameOver,
manager.SkipShowingChoicesFrames,
isChoosing,
manager.IsHoveringPerkChoicePanel
);
var tooltipHash = (manager.HoveredPerkType != null || manager.HoveredShopItem.HasValue) ? Mouse.Position.GetHashCode() : 0;
var hash2 = System.HashCode.Combine(
player.IsValid(),
manager.IsSpectator,
manager.SelectedPlayer,
playerHash,
manager.HoveredPlayer,
manager.HoveredPlayerIcon,
manager.HoveredPerkType,
System.HashCode.Combine(
manager.HoveredPerkLevel,
manager.IsHoveredPerkAChoice,
manager.HoveredPerkViewedPlayer,
manager.HoveredPerkChoiceSlot,
manager.HoveredShopItem?.Id,
manager.ShowBossHealthbar,
tooltipHash
)
);
return System.HashCode.Combine(
hash1,
hash2,
Input.UsingController,
manager.IsHoveringStatsTab,
ProgressManager.StateVersion,
manager.DashReminderActive,
manager.DashReminderScreenPos.GetHashCode()
);
}
protected override void OnUpdate()
{
base.OnUpdate();
var player = Manager.Instance.LocalPlayer;
if ( player.IsValid() )
{
var isInLobby = player.IsInLobby;
var isDead = player.IsDead;
var m2 = Manager.Instance;
if ( isDead )
m2.DashReminderIdleTime = 0f;
else
m2.DashReminderIdleTime += Time.Delta;
// Hide if dead
if ( isDead && m2.DashReminderActive )
m2.DashReminderActive = false;
// If the player has ever dashed, permanently suppress the reminder
if ( player.HasEverDashed && m2.DashReminderActive )
m2.DashReminderActive = false;
// Show once per run, never if they've dashed this session
if ( !player.HasEverDashed && !m2.DashReminderShownThisRun && !m2.DashReminderActive )
{
if ( m2.GameState == GameState.Playing &&
!isInLobby &&
!isDead &&
m2.DashReminderIdleTime > 90f &&
(player.NumDashesAvailable > 0 || player.NumTempDashesAvailable > 0) )
{
m2.DashReminderShownThisRun = true;
m2.DashReminderActive = true;
}
}
if ( m2.DashReminderActive )
{
var playerScreenPos = Scene.Camera.PointToScreenPixels( player.WorldPosition );
m2.DashReminderScreenPos = playerScreenPos;
}
}
if ( !Input.UsingController )
{
var m = Manager.Instance;
var p = m.LocalPlayer;
var hideCursor =
m.GameState != GameState.Lobby &&
!m.IsSpectator &&
!m.IsPaused &&
!m.IsHoveringPerkChoicePanel &&
!m.IsHoveringStatsTab &&
!m.IsEscMenuOpen &&
!m.IsOptionsMenuOpen &&
!m.ShouldShowGameOverScreen &&
!m.ShouldShowQuestProgressPanel &&
(m.HoveredPerkType == null || m.IsHoveredPerkFromWorldItem);
Mouse.CursorType = hideCursor ? "none" : "";
}
if ( Input.EscapePressed || (Input.UsingController && Input.Pressed("Back") ))
{
if ( Manager.Instance.PlayerProfileToShow is not null)
{
Manager.Instance.PlayerProfileToShow = null;
Input.EscapePressed = false;
}
else if ( Manager.Instance.RunEntryToShow is not null)
{
Manager.Instance.RunEntryToShow = null;
Input.EscapePressed = false;
}
}
}
}