UI/Player/Info.razor
@using Sandbox.UI
@using Sandbox.UI.Components
@inherits Panel
<style>
Info {
position: absolute;
width: 100%;
bottom: 0;
height: 100%;
align-items: center;
justify-content: center;
transition: all 0.5s sin-ease-in-out;
flex-direction: row;
}
.arc-root {
position: absolute;
width: 600px;
height: 600px;
margin-bottom: 16px;
margin-right: 12px;
}
.seg {
position: absolute;
opacity: 0.22;
width: 10px;
height: 18px;
}
.seg.health {
background-color: rgba(95, 230, 120, 0.95);
}
.seg.health.active {
opacity: 1;
box-shadow: 0 0 8px rgba(95, 230, 120, 0.45);
}
.seg.armour {
background-color: rgba(65, 195, 255, 0.95);
}
.seg.armour.active {
opacity: 1;
box-shadow: 0 0 8px rgba(65, 195, 255, 0.4);
}
.arc-label {
position: absolute;
font-family: "Wallpoet";
font-size: 24px;
color: rgba(210, 245, 255, 0.9);
text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
}
.arc-label.hp {
left: 144px;
bottom: 122px;
}
.arc-label.sh {
right: 144px;
bottom: 122px;
}
.sWarning {
text-align: center;
position: absolute;
font-family: "Wallpoet";
font-size: 44px;
top: 22%;
left: 50%;
transform: translate(-50%, -50%);
color: rgba(39, 181, 238, 1);
z-index: 30;
}
</style>
<root>
@if ( _showWarning )
{
<label class="sWarning">- -[WARNING SHIELD DOWN]- -</label>
}
<div class="arc-root">
@foreach ( var s in GetHealthArc() )
{
<div class="seg health @(s.Active ? "active" : "")"
style="left:@($"{s.X:F1}px"); top:@($"{s.Y:F1}px"); transform:rotate(@($"{s.Angle + 90f:F1}deg"));"></div>
}
@foreach ( var s in GetShieldArc() )
{
<div class="seg armour @(s.Active ? "active" : "")"
style="left:@($"{s.X:F1}px"); top:@($"{s.Y:F1}px"); transform:rotate(@($"{s.Angle + 90f:F1}deg"));"></div>
}
<div class="arc-label hp">HP @MathF.Round(_health)</div>
<div class="arc-label sh">SH @MathF.Round(_shield)</div>
</div>
</root>
@code
{
private PlayerPawn Pawn => LocalPlayer.Pawn;
private float _health => Pawn?.Health ?? 0f;
private float _healthMax => Pawn?.MaxHealth ?? 100f;
private float _shield => Pawn?.Shield ?? 0f;
private float _shieldMax => Pawn?.MaxShield ?? 0f;
private bool _showWarning =>
PilotGame.Gamemode != FPGameMode.Instagib &&
(Pawn?.IsAlive ?? false) &&
_health > 0f &&
_shieldMax > 0f &&
_shield <= 0f;
private float HealthProgress => _healthMax > 0f ? Math.Clamp( _health / _healthMax, 0f, 1f ) : 0f;
private float ShieldProgress => _shieldMax > 0f ? Math.Clamp( _shield / _shieldMax, 0f, 1f ) : 0f;
private readonly struct ArcSeg
{
public ArcSeg( float x, float y, float angle, bool active )
{
X = x;
Y = y;
Angle = angle;
Active = active;
}
public float X { get; }
public float Y { get; }
public float Angle { get; }
public bool Active { get; }
}
private IEnumerable<ArcSeg> GetHealthArc() => BuildArc( HealthProgress, 256f, 135, 225f, 28);
private IEnumerable<ArcSeg> GetShieldArc() => BuildArc( ShieldProgress, 256f, 45, -45f, 28 );
private static IEnumerable<ArcSeg> BuildArc( float progress, float radius, float start, float end, int count )
{
const float cx = 300f;
const float cy = 300f;
for ( int i = 0; i < count; i++ )
{
var t = count <= 1 ? 1f : i / (float)(count - 1);
var angle = start + (end - start) * t;
var rad = angle.DegreeToRadian();
var x = cx + MathF.Cos( rad ) * radius;
var y = cy + MathF.Sin( rad ) * radius;
yield return new ArcSeg( x, y, angle, (i + 1) / (float)count <= progress );
}
}
protected override int BuildHash()
=> HashCode.Combine( _health, _shield, _healthMax, _shieldMax, Pawn?.IsAlive );
}