champions-of-luneraria/Assets/Luneraria/UI/Hud/LuneElements.cs
Martin Stickelbroeck 2b86c0d3b5 U3B/U3C Zwischenstand: HUD-Bereiche, Eingabe und Befehle
Das G3-HUD steht im Spiel; die Abnahme (U3D) folgt.

- U3B: alle Bereiche und Komponenten aus Godot übertragen (LuneHud,
  Lanes, Bossgrotte, nächste Welle, Kaufkarten, Armee, Held mit Items und
  Talenten, Angriffschampions, Team, Ereignisse, Minimap, Tooltip,
  Optionen, Partieende). Porträts aus den echten Modellen, einmal gerendert.
- Offene G3-Punkte umgesetzt: „1 WELLE“ als Band am Porträtrand,
  Schadens- und Rüstungssymbole auf den Karten, Armeewert, Heldenporträts
  auf der Minimap. Befehlsplatz für die Ultimates der geplanten Helden
  reserviert (verborgen).
- U3C: LuneGame verdrahtet Befehle (gleiche Schnittstelle wie die Bots),
  Bereit mit 1-s-Sperre nach dem Kampf, Tempo und Pause, Tasten wie in G3,
  Kamera-Ziele, Auswahl im Feld, Planungsvorschau und Aufstellungsflächen.
  Ersetzt die U2-Debug-Anzeige. Bot-Partien und Benchmark unverändert.
- Szenen- und Bibliotheksbauer idempotent: Animator-Controller und
  Post-Processing-Profil werden an Ort und Stelle aktualisiert. Die Szene
  bekommt bei jedem Bau neue Objekt-IDs, der Inhalt ist gleich.
- Prüfung (Port von verify_g3, Teile A–H): 699 Prüfungen ohne Fehler.
  EditMode 23/23, PlayMode 12/12, Fingerabdrücke der Bot-Partien
  unverändert (DCE95C96556F3A1A, 7B8E43AEFC0D60C5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 01:45:02 +02:00

682 lines
27 KiB
C#

using System;
using UnityEngine;
using UnityEngine.UIElements;
namespace Luneraria.UI
{
/// <summary>The plate styles of the HUD panels (Godot "panel" styleboxes of lune_ui_theme.gd and the menu theme).</summary>
public static class LunePanels
{
public static readonly LunePlateStyle Hud = LunePlateStyle.Hud(LuneUiTheme.SlateTop, LuneUiTheme.SlateBottom, LuneUiTheme.Edge, 1f, 6f, 12f, 10f);
public static readonly LunePlateStyle HudTight = LunePlateStyle.Hud(LuneUiTheme.SlateTop, LuneUiTheme.SlateBottom, LuneUiTheme.Edge, 1f, 5f, 8f, 6f);
public static readonly LunePlateStyle Modal = LunePlateStyle.Hud(new Color(.07f, .09f, .125f, .98f), new Color(.045f, .06f, .085f, .98f), LuneUiTheme.Hex("5c4a33"), 1.5f, 10f, 22f, 18f)
.WithShadow(new Color(0f, 0f, 0f, .55f), 16f);
public static readonly LunePlateStyle BannerDanger = LunePlateStyle.Hud(new Color(.16f, .075f, .05f, .94f), new Color(.11f, .05f, .035f, .95f), LuneUiTheme.Hex("a0523a"), 1.5f, 6f, 16f, 8f);
public static readonly LunePlateStyle BannerOwn = LunePlateStyle.Hud(new Color(.07f, .1f, .16f, .94f), new Color(.05f, .07f, .11f, .95f), LuneUiTheme.Hex("5f86b8"), 1.5f, 6f, 16f, 8f);
public static readonly LunePlateStyle Tooltip = LunePlateStyle.Hud(new Color(.06f, .08f, .11f, .98f), new Color(.045f, .06f, .085f, .98f), LuneUiTheme.Hex("6b5a44"), 1f, 6f, 12f, 9f)
.WithShadow(new Color(0f, 0f, 0f, .5f), 8f);
public static readonly LunePlateStyle Menu = MenuPlate();
static LunePlateStyle MenuPlate()
{
LunePlateStyle style = LunePlateStyle.Menu(new Color(.078f, .094f, .114f, .95f), new Color(.055f, .066f, .08f, .96f), LuneUiTheme.Hex("5c4a33"), 1.5f, 12f, 30f, 26f)
.WithShadow(new Color(0f, 0f, 0f, .45f), 18f);
style.Ornaments = true;
style.OrnamentColor = LuneUiTheme.Hex("8a6a3c");
return style;
}
}
/// <summary>A panel with a drawn plate background (Godot PanelContainer with a ForgedStyle).</summary>
public class LunePanel : VisualElement
{
LunePlateStyle _plate;
public LunePanel(LunePlateStyle plate, bool stopsMouse = true)
{
pickingMode = stopsMouse ? PickingMode.Position : PickingMode.Ignore;
Plate = plate;
generateVisualContent += context =>
{
if (_plate != null)
LunePlates.Draw(context, new Rect(0f, 0f, layout.width, layout.height), _plate);
};
}
public LunePlateStyle Plate
{
get { return _plate; }
set
{
if (value == _plate)
return;
_plate = value;
value?.ApplyPadding(this);
MarkDirtyRepaint();
}
}
}
/// <summary>Look of a button variation: plates per state and the text colours (Godot Button theme items).</summary>
public sealed class LuneButtonLook
{
public LunePlateStyle Normal;
public LunePlateStyle Hover;
public LunePlateStyle Pressed;
public LunePlateStyle Disabled;
public LunePlateStyle HoverPressed;
/// <summary>Flat tab look: background and bottom line per state instead of plates.</summary>
public bool Tab;
public string TextVariation = "HudText";
public float TextSize = 15f;
public Color Text = LuneUiTheme.Text;
public Color TextHover = Color.white;
public Color? TextPressed;
public Color? TextHoverPressed;
public Color TextDisabled = LuneUiTheme.Faint;
public LunePlateStyle PlateFor(bool disabled, bool pressed, bool hover)
{
if (disabled)
return Disabled ?? Normal;
if (pressed && hover)
return HoverPressed ?? Pressed ?? Normal;
if (pressed)
return Pressed ?? Normal;
if (hover)
return Hover ?? Normal;
return Normal;
}
public Color TextFor(bool disabled, bool pressed, bool hover)
{
if (disabled)
return TextDisabled;
if (pressed && hover)
return TextHoverPressed ?? TextPressed ?? TextHover;
if (pressed)
return TextPressed ?? Text;
return hover ? TextHover : Text;
}
}
/// <summary>The button variations of lune_ui_theme.gd and the menu theme.</summary>
public static class LuneButtons
{
static LunePlateStyle P(string top, string bottom, Color edge, float width, float cut, float x, float y)
{
return LunePlateStyle.Hud(LuneUiTheme.Hex(top), LuneUiTheme.Hex(bottom), edge, width, cut, x, y);
}
static LunePlateStyle P(Color top, Color bottom, Color edge, float width, float cut, float x, float y)
{
return LunePlateStyle.Hud(top, bottom, edge, width, cut, x, y);
}
static LunePlateStyle M(string top, string bottom, Color edge, float width, float cut, float x, float y)
{
return LunePlateStyle.Menu(LuneUiTheme.Hex(top), LuneUiTheme.Hex(bottom), edge, width, cut, x, y);
}
static Color H(string hex) { return LuneUiTheme.Hex(hex); }
public static readonly LuneButtonLook HudButton = new LuneButtonLook
{
Normal = P("1b232d", "131920", H("4a5a6c"), 1f, 5f, 12f, 7f), Hover = P("26303c", "1a212a", LuneUiTheme.BronzeLight, 1f, 5f, 12f, 7f),
Pressed = P("10151b", "171d25", LuneUiTheme.Bronze, 1.5f, 5f, 12f, 7f), Disabled = P("141a20", "10141a", H("2c333c"), 1f, 5f, 12f, 7f),
TextSize = 15f,
};
public static readonly LuneButtonLook HudIconButton = new LuneButtonLook
{
Normal = P("1b232d", "131920", H("3c4a5a"), 1f, 5f, 9f, 6f), Hover = P("26303c", "1a212a", LuneUiTheme.BronzeLight, 1f, 5f, 9f, 6f),
Pressed = P("10151b", "171d25", LuneUiTheme.Bronze, 1.5f, 5f, 9f, 6f), Disabled = P("141a20", "10141a", H("2c333c"), 1f, 5f, 9f, 6f),
TextSize = 15f,
};
public static readonly LuneButtonLook HudPrimary = new LuneButtonLook
{
Normal = M("e8c47e", "b98a4a", H("fff0cc"), 1.5f, 7f, 16f, 8f), Hover = M("f6d898", "cc9c56", Color.white, 1.5f, 7f, 16f, 8f),
Pressed = M("b98a4a", "d5aa66", H("fff0cc"), 2f, 7f, 16f, 8f), Disabled = M("4a4234", "3a3328", H("6a5c46"), 1f, 7f, 16f, 8f),
TextVariation = "HudValue", TextSize = 16f, Text = LuneUiTheme.Ink, TextHover = LuneUiTheme.Ink, TextPressed = LuneUiTheme.Ink, TextDisabled = H("9c8f78"),
};
/// <summary>Godot PrimaryButton of the menu theme (Bereit, Neue Partie).</summary>
public static readonly LuneButtonLook Primary = new LuneButtonLook
{
Normal = M("e8c47e", "b98a4a", H("fff0cc"), 1.5f, 9f, 30f, 15f), Hover = M("f6d898", "cc9c56", Color.white, 1.5f, 9f, 30f, 15f),
Pressed = M("b98a4a", "d5aa66", H("fff0cc"), 2f, 9f, 30f, 15f), Disabled = M("5a4c38", "463a2a", H("7d6a4c"), 1.5f, 9f, 30f, 15f),
TextVariation = "HudName", TextSize = 22f, Text = LuneUiTheme.Ink, TextHover = LuneUiTheme.Ink, TextPressed = LuneUiTheme.Ink, TextDisabled = H("2a2218"),
};
/// <summary>Godot Button of the menu theme (secondary).</summary>
public static readonly LuneButtonLook Secondary = new LuneButtonLook
{
Normal = M("1d232b", "141920", H("6b5a44"), 1.5f, 7f, 20f, 11f), Hover = M("27303a", "1a2028", LuneUiTheme.BronzeLight, 1.5f, 7f, 20f, 11f),
Pressed = M("10141a", "171c23", LuneUiTheme.Bronze, 2f, 7f, 20f, 11f), Disabled = M("15191e", "111418", H("3a352e"), 1.5f, 7f, 20f, 11f),
TextSize = 17f, Text = LuneUiTheme.Parchment,
};
public static readonly LuneButtonLook HudCard = Card(new LuneButtonLook
{
Normal = P("19222c", "10161d", H("34424f"), 1f, 7f, 6f, 6f), Hover = P("212c38", "141b23", LuneUiTheme.Bronze, 1.5f, 7f, 6f, 6f),
Pressed = P("2a2820", "17171a", LuneUiTheme.BronzeLight, 2f, 7f, 6f, 6f), Disabled = P("12171d", "0d1116", H("252c34"), 1f, 7f, 6f, 6f),
});
public static readonly LuneButtonLook HudChampionCard = Card(new LuneButtonLook
{
Normal = P("261c14", "15100c", H("6e4a2c"), 1f, 7f, 6f, 6f), Hover = P("302216", "1b140e", LuneUiTheme.Champion, 1.5f, 7f, 6f, 6f),
Pressed = P("3a2818", "1e150e", H("ffc88c"), 2f, 7f, 6f, 6f), Disabled = P("16110d", "0f0c0a", H("3a2c20"), 1f, 7f, 6f, 6f),
});
public static readonly LuneButtonLook HudChip = Card(new LuneButtonLook
{
Normal = P("1a232d", "11171e", H("33404d"), 1f, 4f, 2f, 2f), Hover = P("222d39", "151c24", LuneUiTheme.Bronze, 1f, 4f, 2f, 2f),
Pressed = P("2c2a22", "17171a", LuneUiTheme.BronzeLight, 2f, 4f, 2f, 2f), Disabled = P("12171d", "0d1116", H("252c34"), 1f, 4f, 2f, 2f),
});
public static readonly LuneButtonLook HudRow = new LuneButtonLook
{
Normal = P(new Color(.09f, .12f, .16f, .55f), new Color(.06f, .08f, .11f, .55f), H("2a3645"), 1f, 5f, 10f, 7f),
Hover = P(new Color(.13f, .17f, .22f, .75f), new Color(.08f, .11f, .15f, .75f), LuneUiTheme.Bronze, 1f, 5f, 10f, 7f),
Pressed = P(new Color(.16f, .15f, .12f, .8f), new Color(.09f, .09f, .1f, .8f), LuneUiTheme.BronzeLight, 1.5f, 5f, 10f, 7f),
Disabled = P(new Color(.09f, .12f, .16f, .55f), new Color(.06f, .08f, .11f, .55f), H("2a3645"), 1f, 5f, 10f, 7f),
};
public static readonly LuneButtonLook HudToggle = Toggle();
static LuneButtonLook Toggle()
{
LunePlateStyle on = P("3a3222", "262117", LuneUiTheme.BronzeLight, 1.5f, 5f, 10f, 6f).WithInner(LuneUiTheme.WithAlpha(LuneUiTheme.BronzeLight, .35f));
return new LuneButtonLook
{
Normal = P("171e27", "11161d", H("3a4756"), 1f, 5f, 10f, 6f), Hover = P("212a35", "171d25", LuneUiTheme.Bronze, 1f, 5f, 10f, 6f),
Pressed = on, HoverPressed = on, Disabled = P("11151a", "0e1115", H("242a31"), 1f, 5f, 10f, 6f),
TextVariation = "HudBadge", TextSize = 14f, Text = H("cfd5dc"), TextHover = Color.white, TextPressed = H("fff4dc"), TextHoverPressed = H("fff4dc"), TextDisabled = H("59616b"),
};
}
public static readonly LuneButtonLook HudTab = new LuneButtonLook
{
Tab = true, TextVariation = "HudCaps", TextSize = 14f, Text = LuneUiTheme.Muted, TextHover = LuneUiTheme.Text, TextPressed = LuneUiTheme.BronzeLight,
TextHoverPressed = LuneUiTheme.BronzeLight, TextDisabled = LuneUiTheme.Faint,
};
static LuneButtonLook Card(LuneButtonLook look)
{
look.HoverPressed = look.Pressed;
return look;
}
}
/// <summary>
/// Button of the HUD (Godot Button with a theme variation). Draws the plate of its state;
/// `Disabled` greys it out and blocks clicks but keeps hover, so tooltips can say why (as
/// in G3). Toggle buttons keep their pressed state.
/// </summary>
public class LuneButton : VisualElement
{
public event Action Clicked;
public readonly Label TextLabel;
public bool ToggleMode;
LuneButtonLook _look;
bool _hover;
bool _down;
bool _on;
bool _disabled;
public LuneButton(string text, LuneButtonLook look, VisualElement parent = null)
{
_look = look;
focusable = false;
style.flexDirection = FlexDirection.Row;
style.alignItems = Align.Center;
style.justifyContent = Justify.Center;
if (text != null)
{
TextLabel = LuneUi.Label(text, look.TextVariation, this, look.TextSize);
TextLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
}
ApplyPadding();
generateVisualContent += Draw;
RegisterCallback<PointerEnterEvent>(_ => SetHover(true));
RegisterCallback<PointerLeaveEvent>(_ => SetHover(false));
RegisterCallback<PointerDownEvent>(OnDown);
RegisterCallback<PointerUpEvent>(OnUp);
RegisterCallback<PointerCaptureOutEvent>(_ => { _down = false; Refresh(); });
Refresh();
parent?.Add(this);
}
public LuneButtonLook Look
{
get { return _look; }
set
{
if (value == _look)
return;
_look = value;
if (TextLabel != null)
LuneUi.Apply(TextLabel, value.TextVariation, value.TextSize);
ApplyPadding();
Refresh();
}
}
public string Text
{
get { return TextLabel != null ? TextLabel.text : ""; }
set
{
if (TextLabel != null && TextLabel.text != value)
TextLabel.text = value;
}
}
public bool Disabled
{
get { return _disabled; }
set
{
if (value == _disabled)
return;
_disabled = value;
Refresh();
}
}
/// <summary>Pressed state of a toggle (Godot set_pressed_no_signal).</summary>
public bool On
{
get { return _on; }
set
{
if (value == _on)
return;
_on = value;
Refresh();
}
}
public bool Hovered { get { return _hover; } }
void ApplyPadding()
{
if (_look.Tab)
{
style.paddingLeft = 12f;
style.paddingRight = 12f;
style.paddingTop = 6f;
style.paddingBottom = 6f;
}
else
{
_look.Normal?.ApplyPadding(this);
}
}
void SetHover(bool value)
{
_hover = value;
Refresh();
}
void OnDown(PointerDownEvent evt)
{
if (evt.button != 0 || _disabled)
return;
_down = true;
this.CapturePointer(evt.pointerId);
Refresh();
evt.StopPropagation();
}
void OnUp(PointerUpEvent evt)
{
if (evt.button != 0 || !_down)
return;
_down = false;
this.ReleasePointer(evt.pointerId);
bool inside = ContainsPoint(evt.localPosition);
Refresh();
evt.StopPropagation();
if (inside && !_disabled)
Click();
}
/// <summary>Godot `pressed`: toggles first, then raises Clicked (also for tests and hotkeys).</summary>
public void Click()
{
if (_disabled)
return;
if (ToggleMode)
On = !_on;
Clicked?.Invoke();
}
void Refresh()
{
bool pressed = _down || _on;
if (TextLabel != null)
LuneUi.SetColor(TextLabel, _look.TextFor(_disabled, pressed, _hover));
MarkDirtyRepaint();
}
void Draw(MeshGenerationContext context)
{
var rect = new Rect(0f, 0f, layout.width, layout.height);
bool pressed = _down || _on;
if (_look.Tab)
{
Painter2D painter = context.painter2D;
Color fill = pressed ? LuneUiTheme.WithAlpha(LuneUiTheme.BronzeLight, .08f) : _hover && !_disabled ? new Color(1f, 1f, 1f, .04f) : Color.clear;
Color line = pressed ? LuneUiTheme.BronzeLight : _hover && !_disabled ? LuneUiTheme.WithAlpha(LuneUiTheme.Bronze, .5f) : Color.clear;
if (fill.a > 0f)
LunePlates.Polygon(painter, new[] { rect.min, new Vector2(rect.xMax, rect.yMin), rect.max, new Vector2(rect.xMin, rect.yMax) }, fill);
if (line.a > 0f)
LunePlates.Line(painter, new Vector2(rect.xMin, rect.yMax - 1f), new Vector2(rect.xMax, rect.yMax - 1f), line, 2f);
return;
}
LunePlateStyle plate = _look.PlateFor(_disabled, pressed, _hover && !_disabled);
if (plate != null)
LunePlates.Draw(context, rect, plate);
}
}
/// <summary>A LuneGlyphs symbol as an element (Godot LuneGlyph). Ignores the mouse.</summary>
public sealed class LuneGlyphElement : VisualElement
{
string _glyph;
Color _color;
public Color Accent;
public LuneGlyphElement(string glyph, Color color, float edge = 18f)
{
_glyph = glyph;
_color = color;
pickingMode = PickingMode.Ignore;
style.width = edge;
style.height = edge;
style.flexShrink = 0f;
style.alignSelf = Align.Center;
generateVisualContent += context =>
{
if (!string.IsNullOrEmpty(_glyph))
LuneGlyphs.Draw(context.painter2D, _glyph, new Rect(0f, 0f, layout.width, layout.height), _color, Accent);
};
}
public string Glyph
{
get { return _glyph; }
set
{
if (value == _glyph)
return;
_glyph = value;
MarkDirtyRepaint();
}
}
public Color Color
{
get { return _color; }
set
{
if (value == _color)
return;
_color = value;
MarkDirtyRepaint();
}
}
public float Edge
{
set
{
style.width = value;
style.height = value;
}
}
}
/// <summary>Thin value bar drawn directly: dark track, fill, a top highlight and optional ticks (Godot LuneHpBar).</summary>
public sealed class LuneHpBar : VisualElement
{
float _fraction = 1f;
Color _fill = LuneUiTheme.Good;
public int Ticks;
public Color Track = new Color(0f, 0f, 0f, .55f);
public LuneHpBar(float width = 60f, float height = 6f)
{
pickingMode = PickingMode.Ignore;
style.minWidth = width;
style.height = height;
style.flexShrink = 0f;
generateVisualContent += Draw;
}
public float Fraction
{
get { return _fraction; }
set
{
value = Mathf.Clamp01(value);
if (Mathf.Abs(value - _fraction) <= .0005f)
return;
_fraction = value;
MarkDirtyRepaint();
}
}
public Color Fill
{
get { return _fill; }
set
{
if (value == _fill)
return;
_fill = value;
MarkDirtyRepaint();
}
}
void Draw(MeshGenerationContext context)
{
Painter2D painter = context.painter2D;
var rect = new Rect(0f, 0f, layout.width, layout.height);
LunePlates.Polygon(painter, Corners(rect), Track);
if (_fraction > 0f)
{
var filled = new Rect(0f, 0f, Mathf.Max(1f, rect.width * _fraction), rect.height);
LunePlates.Polygon(painter, Corners(filled), _fill);
LunePlates.Line(painter, new Vector2(0f, .5f), new Vector2(filled.width, .5f), new Color(1f, 1f, 1f, .28f), 1f);
}
for (int index = 1; index < Ticks; index++)
{
float x = Mathf.Round(rect.width * index / Ticks);
LunePlates.Line(painter, new Vector2(x, 0f), new Vector2(x, rect.height), new Color(0f, 0f, 0f, .45f), 1f);
}
LunePlates.Outline(painter, Corners(new Rect(.5f, .5f, rect.width - 1f, rect.height - 1f)), new Color(1f, 1f, 1f, .12f), 1f);
}
public static Vector2[] Corners(Rect rect)
{
return new[] { rect.min, new Vector2(rect.xMax, rect.yMin), rect.max, new Vector2(rect.xMin, rect.yMax) };
}
/// <summary>Green above 60 %, amber above 30 %, red below; the team colour when given.</summary>
public static Color HealthColor(float value, Color? baseColor = null)
{
if (value > .6f)
return baseColor ?? LuneUiTheme.Good;
if (value > .3f)
return LuneUiTheme.Warn;
return LuneUiTheme.Danger;
}
}
/// <summary>
/// One building as a symbol in the team's shape language (round = Blau, angular = Rot) with its
/// HP as a bar underneath (Godot LuneTowerIndicator). Destroyed buildings remain as a hollow
/// outline with a cross.
/// </summary>
public sealed class LuneTowerIndicator : VisualElement
{
public int Team;
public string Tier = "outer";
public bool Alive = true;
public float Fraction = 1f;
public bool Attackable;
public LuneTowerIndicator(float width = 22f, float height = 27f)
{
pickingMode = PickingMode.Position;
style.width = width;
style.height = height;
style.flexShrink = 0f;
generateVisualContent += Draw;
}
public bool SetState(bool alive, float fraction, bool attackable)
{
float value = Mathf.Round(Mathf.Clamp01(fraction) * 100f) / 100f;
if (alive == Alive && Mathf.Approximately(value, Fraction) && attackable == Attackable)
return false;
Alive = alive;
Fraction = value;
Attackable = attackable;
MarkDirtyRepaint();
return true;
}
void Draw(MeshGenerationContext context)
{
Painter2D painter = context.painter2D;
float edge = Mathf.Min(layout.width, layout.height - 5f);
var box = new Rect((layout.width - edge) * .5f, 0f, edge, edge);
Color tint = LuneUiTheme.TeamColor(Team);
string shape = "shape_" + Mathf.Clamp(Team, 0, 1);
if (Alive)
{
LuneGlyphs.Draw(painter, shape, box, LuneUiTheme.Lightened(LuneUiTheme.TeamDark[Mathf.Clamp(Team, 0, 1)], .1f));
LuneGlyphs.Draw(painter, shape, Grow(box, -edge * .08f), LuneUiTheme.WithAlpha(tint, .35f));
LuneGlyphs.Draw(painter, Tier == "core" ? "ancient" : "tower", Grow(box, -edge * .24f), LuneUiTheme.Hex("f4efe4"));
var bar = new Rect(box.x + 1f, layout.height - 3f, box.width - 2f, 3f);
LunePlates.Polygon(painter, LuneHpBar.Corners(bar), new Color(0f, 0f, 0f, .6f));
if (Fraction > 0f)
LunePlates.Polygon(painter, LuneHpBar.Corners(new Rect(bar.x, bar.y, bar.width * Fraction, bar.height)), LuneHpBar.HealthColor(Fraction, tint));
}
else
{
LuneGlyphs.Draw(painter, shape, box, new Color(0f, 0f, 0f, .35f));
LuneGlyphs.Draw(painter, "close", Grow(box, -edge * .18f), new Color(.5f, .52f, .55f, .8f));
}
}
static Rect Grow(Rect rect, float amount)
{
return new Rect(rect.x - amount, rect.y - amount, rect.width + amount * 2f, rect.height + amount * 2f);
}
}
/// <summary>
/// Small labelled state (RUHIG, KRITISCH, BEREIT, BOT …; Godot LuneStatusBadge). Always colour
/// plus text and, where it helps, a symbol; never colour alone.
/// </summary>
public sealed class LuneStatusBadge : VisualElement
{
readonly LuneGlyphElement _glyph;
readonly Label _label;
public string Text { get; private set; } = "";
Color _color;
bool _filled;
public LuneStatusBadge()
{
pickingMode = PickingMode.Ignore;
style.flexDirection = FlexDirection.Row;
style.alignItems = Align.Center;
style.alignSelf = Align.Center;
style.flexShrink = 0f;
style.paddingLeft = 7f;
style.paddingRight = 7f;
style.paddingTop = 1f;
style.paddingBottom = 1f;
style.borderTopLeftRadius = 4f;
style.borderTopRightRadius = 4f;
style.borderBottomLeftRadius = 4f;
style.borderBottomRightRadius = 4f;
style.borderLeftWidth = 1f;
style.borderRightWidth = 1f;
style.borderTopWidth = 1f;
style.borderBottomWidth = 1f;
_glyph = LuneUi.Glyph("", Color.white, 12f, this);
_glyph.style.marginRight = 4f;
LuneUi.Show(_glyph, false);
_label = LuneUi.Label("", "HudBadge", this);
}
public void SetState(string text, Color color, string glyph = "", bool filled = false)
{
if (text == Text && color == _color && filled == _filled && glyph == _glyph.Glyph)
return;
Text = text;
_color = color;
_filled = filled;
_label.text = text;
Color ink = filled ? LuneUiTheme.Hex("15110b") : LuneUiTheme.Lightened(color, .15f);
LuneUi.SetColor(_label, ink);
LuneUi.Show(_glyph, !string.IsNullOrEmpty(glyph));
_glyph.Glyph = glyph;
_glyph.Color = ink;
Color background = filled ? LuneUiTheme.WithAlpha(color, .92f) : LuneUiTheme.WithAlpha(LuneUiTheme.Darkened(color, .75f), .85f);
Color border = filled ? LuneUiTheme.Lightened(color, .2f) : color;
style.backgroundColor = background;
style.borderLeftColor = border;
style.borderRightColor = border;
style.borderTopColor = border;
style.borderBottomColor = border;
}
}
/// <summary>Tabs with a bronze underline for the open one (Godot LuneTabBar). Select changes only the display; TabChanged fires on input.</summary>
public sealed class LuneTabBar : VisualElement
{
public event Action<int> TabChanged;
public int Current { get; private set; }
public readonly System.Collections.Generic.List<LuneButton> Tabs = new System.Collections.Generic.List<LuneButton>();
public LuneTabBar()
{
pickingMode = PickingMode.Ignore;
style.flexDirection = FlexDirection.Row;
}
public LuneButton AddTab(string title, string tipTitle = "", string tipText = "")
{
var tab = new LuneButton(title, LuneButtons.HudTab, this);
int index = Tabs.Count;
if (index > 0)
tab.style.marginLeft = 2f;
tab.Clicked += () =>
{
Select(index);
TabChanged?.Invoke(index);
};
if (tipTitle.Length > 0)
LuneTooltip.Attach(tab, () => new LuneTip { Title = tipTitle, Text = tipText });
Tabs.Add(tab);
tab.On = index == 0;
return tab;
}
public void Select(int index)
{
Current = Mathf.Clamp(index, 0, Tabs.Count - 1);
for (int i = 0; i < Tabs.Count; i++)
Tabs[i].On = i == Current;
}
}
}