UI: redesign inspect panel — stat bars, sections, scroll, clearer tabs
Replace the single per-tab text blob with structured widgets: - StatBar: RimWorld-style colored bars for needs, skills, capacities, blood/pain, mood. Good→bad gradient (red/amber/green); drives & skills use a neutral blue; pain fills by amount but colors inverted (more pain = red). - Section headers (accent) group Health (vitals/capacities/conditions) and Mood; section keys lose the "— … —" dashes (the header styling separates them now). - Body lives in a fixed-height ScrollViewer so long tabs (Health) scroll and the panel keeps a constant size instead of jumping between tabs. - Active tab gets a background highlight, not just accent text; tab buttons padded. - Narrative data (overview/genes/products) stays as styled lines; plant growth-to- next-stage shown as a bar. New loc: inspect.health.vitals, inspect.vital.blood/pain (ru/en). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
56f2c22932
commit
02dcd01c0f
@@ -27,7 +27,7 @@
|
|||||||
"inspect.state.dormanthot": "dormant — too hot",
|
"inspect.state.dormanthot": "dormant — too hot",
|
||||||
"inspect.stress": "Stress: {0:0.0} d to death",
|
"inspect.stress": "Stress: {0:0.0} d to death",
|
||||||
"inspect.temp": "Temperature: grows {0:0}…{1:0}°C, optimal {2:0}…{3:0}°C",
|
"inspect.temp": "Temperature: grows {0:0}…{1:0}°C, optimal {2:0}…{3:0}°C",
|
||||||
"inspect.genes": "— Genes —",
|
"inspect.genes": "Genes",
|
||||||
"inspect.gene.vigor": "Vigor {0:0.00} · lifespan {1:0} d",
|
"inspect.gene.vigor": "Vigor {0:0.00} · lifespan {1:0} d",
|
||||||
"inspect.gene.env": "Opt. light {0:0.00} · opt. soil {1:0.00}",
|
"inspect.gene.env": "Opt. light {0:0.00} · opt. soil {1:0.00}",
|
||||||
"inspect.gene.repro": "Dispersal {0:0.0} · interval {1:0.0} d",
|
"inspect.gene.repro": "Dispersal {0:0.0} · interval {1:0.0} d",
|
||||||
@@ -48,14 +48,17 @@
|
|||||||
"skill.hunting": "hunting",
|
"skill.hunting": "hunting",
|
||||||
"skill.evasion": "evasion",
|
"skill.evasion": "evasion",
|
||||||
"inspect.health.blood": "Blood: {0:0}% · pain: {1:0}%",
|
"inspect.health.blood": "Blood: {0:0}% · pain: {1:0}%",
|
||||||
"inspect.health.caps": "— Capacities —",
|
"inspect.health.vitals": "Vitals",
|
||||||
|
"inspect.vital.blood": "blood",
|
||||||
|
"inspect.vital.pain": "pain",
|
||||||
|
"inspect.health.caps": "Capacities",
|
||||||
"inspect.health.cap": "{0}: {1:0}%",
|
"inspect.health.cap": "{0}: {1:0}%",
|
||||||
"inspect.health.hediffs": "— Conditions —",
|
"inspect.health.hediffs": "Conditions",
|
||||||
"inspect.health.hediffline": "{0}: severity {1:0}%",
|
"inspect.health.hediffline": "{0}: severity {1:0}%",
|
||||||
"inspect.health.none": "Healthy, no wounds or illness",
|
"inspect.health.none": "Healthy, no wounds or illness",
|
||||||
"inspect.mood.value": "Mood: {0:0}%",
|
"inspect.mood.value": "Mood: {0:0}%",
|
||||||
"inspect.mood.none": "No mood (primitive brain)",
|
"inspect.mood.none": "No mood (primitive brain)",
|
||||||
"inspect.mood.thoughts": "— Thoughts —",
|
"inspect.mood.thoughts": "Thoughts",
|
||||||
"inspect.mood.thoughtline": "{0}: {1}",
|
"inspect.mood.thoughtline": "{0}: {1}",
|
||||||
"inspect.mood.calm": "Calm, nothing on its mind",
|
"inspect.mood.calm": "Calm, nothing on its mind",
|
||||||
"animalstage.baby": "baby",
|
"animalstage.baby": "baby",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"inspect.state.dormanthot": "покой — слишком жарко",
|
"inspect.state.dormanthot": "покой — слишком жарко",
|
||||||
"inspect.stress": "Стресс: {0:0.0} дн до гибели",
|
"inspect.stress": "Стресс: {0:0.0} дн до гибели",
|
||||||
"inspect.temp": "Температура: рост {0:0}…{1:0}°C, оптимум {2:0}…{3:0}°C",
|
"inspect.temp": "Температура: рост {0:0}…{1:0}°C, оптимум {2:0}…{3:0}°C",
|
||||||
"inspect.genes": "— Гены —",
|
"inspect.genes": "Гены",
|
||||||
"inspect.gene.vigor": "Бодрость {0:0.00} · жизнь {1:0} дн",
|
"inspect.gene.vigor": "Бодрость {0:0.00} · жизнь {1:0} дн",
|
||||||
"inspect.gene.env": "Опт. свет {0:0.00} · опт. почва {1:0.00}",
|
"inspect.gene.env": "Опт. свет {0:0.00} · опт. почва {1:0.00}",
|
||||||
"inspect.gene.repro": "Расселение {0:0.0} кл · период {1:0.0} дн",
|
"inspect.gene.repro": "Расселение {0:0.0} кл · период {1:0.0} дн",
|
||||||
@@ -48,14 +48,17 @@
|
|||||||
"skill.hunting": "охота",
|
"skill.hunting": "охота",
|
||||||
"skill.evasion": "уклонение",
|
"skill.evasion": "уклонение",
|
||||||
"inspect.health.blood": "Кровь: {0:0}% · боль: {1:0}%",
|
"inspect.health.blood": "Кровь: {0:0}% · боль: {1:0}%",
|
||||||
"inspect.health.caps": "— Способности —",
|
"inspect.health.vitals": "Жизненные показатели",
|
||||||
|
"inspect.vital.blood": "кровь",
|
||||||
|
"inspect.vital.pain": "боль",
|
||||||
|
"inspect.health.caps": "Способности",
|
||||||
"inspect.health.cap": "{0}: {1:0}%",
|
"inspect.health.cap": "{0}: {1:0}%",
|
||||||
"inspect.health.hediffs": "— Состояния —",
|
"inspect.health.hediffs": "Состояния",
|
||||||
"inspect.health.hediffline": "{0}: тяжесть {1:0}%",
|
"inspect.health.hediffline": "{0}: тяжесть {1:0}%",
|
||||||
"inspect.health.none": "Здоров, ран и болезней нет",
|
"inspect.health.none": "Здоров, ран и болезней нет",
|
||||||
"inspect.mood.value": "Настроение: {0:0}%",
|
"inspect.mood.value": "Настроение: {0:0}%",
|
||||||
"inspect.mood.none": "Настроения нет (примитивный мозг)",
|
"inspect.mood.none": "Настроения нет (примитивный мозг)",
|
||||||
"inspect.mood.thoughts": "— Мысли —",
|
"inspect.mood.thoughts": "Мысли",
|
||||||
"inspect.mood.thoughtline": "{0}: {1}",
|
"inspect.mood.thoughtline": "{0}: {1}",
|
||||||
"inspect.mood.calm": "Спокоен, особых мыслей нет",
|
"inspect.mood.calm": "Спокоен, особых мыслей нет",
|
||||||
"animalstage.baby": "детёныш",
|
"animalstage.baby": "детёныш",
|
||||||
|
|||||||
+221
-143
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
|
||||||
using Friflo.Engine.ECS;
|
using Friflo.Engine.ECS;
|
||||||
using LittleSim.Content;
|
using LittleSim.Content;
|
||||||
using LittleSim.Sim;
|
using LittleSim.Sim;
|
||||||
@@ -18,10 +17,13 @@ namespace LittleSim.UI;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Панель осмотра выбранной сущности (инфо-карточка в стиле RimWorld): угловая рамка вокруг объекта
|
/// Панель осмотра выбранной сущности (инфо-карточка в стиле RimWorld): угловая рамка вокруг объекта
|
||||||
/// (<see cref="SelectionBrackets"/>) и боковая панель в левом нижнем углу с вкладками. Набор вкладок
|
/// (<see cref="SelectionBrackets"/>) и боковая панель в левом нижнем углу с вкладками. Набор вкладок
|
||||||
/// зависит от типа: растение — «Обзор/Гены/Продукты», животное — «Обзор/Нужды/Здоровье/Настроение».
|
/// зависит от типа: растение — «Обзор/Гены/Продукты», животное — «Обзор/Нужды/Навыки/Здоровье/Настроение».
|
||||||
/// Источник выделения — <see cref="Selection"/>; панель только читает компоненты и обновляется каждый
|
/// Источник выделения — <see cref="Selection"/>; панель только читает компоненты и пересобирает тело
|
||||||
/// кадр (<see cref="Refresh"/>), оставаясь живой даже на паузе. Все строки идут через
|
/// активной вкладки каждый кадр (<see cref="Refresh"/>), оставаясь живой даже на паузе. Числовые
|
||||||
/// <see cref="LanguageManager"/> (ключи <c>inspect.*</c>).
|
/// показатели (нужды, навыки, способности, кровь/боль, настроение) рисуются цветными полосами
|
||||||
|
/// (<see cref="StatBar"/>), описательные данные — строками с заголовками-секциями. Тело прокручивается
|
||||||
|
/// (<see cref="ScrollViewer"/> фиксированной высоты), поэтому панель не «прыгает» при смене вкладок.
|
||||||
|
/// Все строки идут через <see cref="LanguageManager"/> (ключи <c>inspect.*</c>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class InspectPanel
|
internal sealed class InspectPanel
|
||||||
{
|
{
|
||||||
@@ -46,6 +48,14 @@ internal sealed class InspectPanel
|
|||||||
Tab.Mood,
|
Tab.Mood,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Палитра тела панели и полос.
|
||||||
|
private static readonly Color Body = new(210, 216, 226);
|
||||||
|
private static readonly Color Warn = new(220, 150, 90);
|
||||||
|
private static readonly Color Neutral = new(86, 132, 178); // нейтральная величина (навык, влечение)
|
||||||
|
private static readonly IBrush ActiveTab = new SolidBrush(new Color(40, 54, 72));
|
||||||
|
|
||||||
|
private const int BarWidth = 300;
|
||||||
|
|
||||||
private readonly EntityStore _store;
|
private readonly EntityStore _store;
|
||||||
private readonly PlantSet _plants;
|
private readonly PlantSet _plants;
|
||||||
private readonly AnimalSet _animals;
|
private readonly AnimalSet _animals;
|
||||||
@@ -58,7 +68,7 @@ internal sealed class InspectPanel
|
|||||||
|
|
||||||
private readonly VerticalStackPanel _panel;
|
private readonly VerticalStackPanel _panel;
|
||||||
private readonly Label _title;
|
private readonly Label _title;
|
||||||
private readonly Label _body;
|
private readonly VerticalStackPanel _bodyPanel;
|
||||||
private readonly List<(Tab Tab, TextButton Button)> _tabs = [];
|
private readonly List<(Tab Tab, TextButton Button)> _tabs = [];
|
||||||
private Tab _tab = Tab.Overview;
|
private Tab _tab = Tab.Overview;
|
||||||
|
|
||||||
@@ -85,9 +95,8 @@ internal sealed class InspectPanel
|
|||||||
_brackets = brackets;
|
_brackets = brackets;
|
||||||
|
|
||||||
_title = new Label { TextColor = Ui.Accent, Wrap = true };
|
_title = new Label { TextColor = Ui.Accent, Wrap = true };
|
||||||
_body = new Label { TextColor = new Color(210, 216, 226), Wrap = true };
|
|
||||||
|
|
||||||
var tabBar = new HorizontalStackPanel { Spacing = 4 };
|
var tabBar = new HorizontalStackPanel { Spacing = 2 };
|
||||||
AddTab(tabBar, Tab.Overview, "inspect.tab.overview");
|
AddTab(tabBar, Tab.Overview, "inspect.tab.overview");
|
||||||
AddTab(tabBar, Tab.Genes, "inspect.tab.genes");
|
AddTab(tabBar, Tab.Genes, "inspect.tab.genes");
|
||||||
AddTab(tabBar, Tab.Products, "inspect.tab.products");
|
AddTab(tabBar, Tab.Products, "inspect.tab.products");
|
||||||
@@ -96,11 +105,21 @@ internal sealed class InspectPanel
|
|||||||
AddTab(tabBar, Tab.Health, "inspect.tab.health");
|
AddTab(tabBar, Tab.Health, "inspect.tab.health");
|
||||||
AddTab(tabBar, Tab.Mood, "inspect.tab.mood");
|
AddTab(tabBar, Tab.Mood, "inspect.tab.mood");
|
||||||
|
|
||||||
|
// Тело вкладки пересобирается каждый кадр; живёт в скролле фиксированной высоты, чтобы панель
|
||||||
|
// держала постоянный размер и не «прыгала» при переключении вкладок и появлении/исчезании строк.
|
||||||
|
_bodyPanel = new VerticalStackPanel { Spacing = 3 };
|
||||||
|
var scroll = new ScrollViewer
|
||||||
|
{
|
||||||
|
Content = _bodyPanel,
|
||||||
|
Height = 280,
|
||||||
|
ShowHorizontalScrollBar = false,
|
||||||
|
};
|
||||||
|
|
||||||
_panel = new VerticalStackPanel
|
_panel = new VerticalStackPanel
|
||||||
{
|
{
|
||||||
Spacing = 6,
|
Spacing = 6,
|
||||||
Padding = new Thickness(14),
|
Padding = new Thickness(14),
|
||||||
Width = 380,
|
Width = 360,
|
||||||
HorizontalAlignment = HorizontalAlignment.Left,
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
VerticalAlignment = VerticalAlignment.Bottom,
|
VerticalAlignment = VerticalAlignment.Bottom,
|
||||||
Margin = new Thickness(12, 0, 0, 14),
|
Margin = new Thickness(12, 0, 0, 14),
|
||||||
@@ -112,7 +131,8 @@ internal sealed class InspectPanel
|
|||||||
_panel.Widgets.Add(_title);
|
_panel.Widgets.Add(_title);
|
||||||
_panel.Widgets.Add(tabBar);
|
_panel.Widgets.Add(tabBar);
|
||||||
_panel.Widgets.Add(new HorizontalSeparator());
|
_panel.Widgets.Add(new HorizontalSeparator());
|
||||||
_panel.Widgets.Add(_body);
|
_panel.Widgets.Add(scroll);
|
||||||
|
_panel.Widgets.Add(new HorizontalSeparator());
|
||||||
_panel.Widgets.Add(
|
_panel.Widgets.Add(
|
||||||
new Label
|
new Label
|
||||||
{
|
{
|
||||||
@@ -128,7 +148,7 @@ internal sealed class InspectPanel
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Подтягивает выделение каждый кадр: проверяет, жива ли сущность (иначе снимает выбор), двигает
|
/// Подтягивает выделение каждый кадр: проверяет, жива ли сущность (иначе снимает выбор), двигает
|
||||||
/// рамку к объекту и пересобирает текст активной вкладки. Осматриваются растения и животные.
|
/// рамку к объекту и пересобирает тело активной вкладки. Осматриваются растения и животные.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Refresh()
|
public void Refresh()
|
||||||
{
|
{
|
||||||
@@ -164,6 +184,7 @@ internal sealed class InspectPanel
|
|||||||
_brackets.Show(center, radius);
|
_brackets.Show(center, radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_bodyPanel.Widgets.Clear();
|
||||||
if (isPlant)
|
if (isPlant)
|
||||||
{
|
{
|
||||||
UpdatePlant(entity);
|
UpdatePlant(entity);
|
||||||
@@ -189,14 +210,18 @@ internal sealed class InspectPanel
|
|||||||
|
|
||||||
private void AddTab(HorizontalStackPanel bar, Tab tab, string labelKey)
|
private void AddTab(HorizontalStackPanel bar, Tab tab, string labelKey)
|
||||||
{
|
{
|
||||||
var button = new TextButton { Text = _content.Languages.Get(labelKey) };
|
var button = new TextButton
|
||||||
|
{
|
||||||
|
Text = _content.Languages.Get(labelKey),
|
||||||
|
Padding = new Thickness(6, 3),
|
||||||
|
};
|
||||||
button.Click += (_, _) => _tab = tab;
|
button.Click += (_, _) => _tab = tab;
|
||||||
bar.Widgets.Add(button);
|
bar.Widgets.Add(button);
|
||||||
_tabs.Add((tab, button));
|
_tabs.Add((tab, button));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Показывает только вкладки текущего типа, подсвечивает активную; если активная не из набора —
|
// Показывает только вкладки текущего типа, подсвечивает активную (цвет + фон); если активная не из
|
||||||
// откатывает на первую (Обзор) при смене типа выбранного объекта.
|
// набора — откатывает на первую (Обзор) при смене типа выбранного объекта.
|
||||||
private void ShowTabs(Tab[] valid)
|
private void ShowTabs(Tab[] valid)
|
||||||
{
|
{
|
||||||
if (Array.IndexOf(valid, _tab) < 0)
|
if (Array.IndexOf(valid, _tab) < 0)
|
||||||
@@ -206,11 +231,57 @@ internal sealed class InspectPanel
|
|||||||
|
|
||||||
foreach (var (tab, button) in _tabs)
|
foreach (var (tab, button) in _tabs)
|
||||||
{
|
{
|
||||||
button.Visible = Array.IndexOf(valid, tab) >= 0;
|
var visible = Array.IndexOf(valid, tab) >= 0;
|
||||||
button.TextColor = tab == _tab ? Ui.Accent : Ui.Muted;
|
button.Visible = visible;
|
||||||
|
var active = tab == _tab;
|
||||||
|
button.TextColor = active ? Ui.Accent : Ui.Muted;
|
||||||
|
button.Background = active ? ActiveTab : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// — Виджеты-кирпичики тела вкладки —
|
||||||
|
|
||||||
|
private void Header(string key) =>
|
||||||
|
_bodyPanel.Widgets.Add(
|
||||||
|
new Label
|
||||||
|
{
|
||||||
|
Text = _content.Languages.Get(key),
|
||||||
|
TextColor = Ui.Accent,
|
||||||
|
Margin = new Thickness(0, 6, 0, 1),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
private void Line(string text, Color? color = null) =>
|
||||||
|
_bodyPanel.Widgets.Add(
|
||||||
|
new Label
|
||||||
|
{
|
||||||
|
Text = text,
|
||||||
|
TextColor = color ?? Body,
|
||||||
|
Wrap = true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Полоса показателя: ширина заливки = доля, цвет задаётся вызывающим (см. GoodBad/Neutral).
|
||||||
|
private void Bar(string name, float fraction, Color color, string? value = null) =>
|
||||||
|
_bodyPanel.Widgets.Add(
|
||||||
|
new StatBar(BarWidth, name, fraction, value ?? Pct(fraction), color)
|
||||||
|
);
|
||||||
|
|
||||||
|
private static string Pct(float fraction) =>
|
||||||
|
((int)MathF.Round(Math.Clamp(fraction, 0f, 1f) * 100f)).ToString() + "%";
|
||||||
|
|
||||||
|
// Цвет «хорошо↔плохо»: 0 — красный, 0.5 — янтарный, 1 — зелёный (приглушённые, чтобы белый текст читался).
|
||||||
|
private static Color GoodBad(float fraction)
|
||||||
|
{
|
||||||
|
fraction = Math.Clamp(fraction, 0f, 1f);
|
||||||
|
var bad = new Color(176, 72, 66);
|
||||||
|
var mid = new Color(176, 146, 58);
|
||||||
|
var good = new Color(86, 158, 96);
|
||||||
|
return fraction < 0.5f
|
||||||
|
? Color.Lerp(bad, mid, fraction * 2f)
|
||||||
|
: Color.Lerp(mid, good, (fraction - 0.5f) * 2f);
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdatePlant(Entity entity)
|
private void UpdatePlant(Entity entity)
|
||||||
{
|
{
|
||||||
var languages = _content.Languages;
|
var languages = _content.Languages;
|
||||||
@@ -223,21 +294,18 @@ internal sealed class InspectPanel
|
|||||||
_title.Text = languages.Get(def.Label) + (traits.IsVariant ? " *" : "");
|
_title.Text = languages.Get(def.Label) + (traits.IsVariant ? " *" : "");
|
||||||
ShowTabs(PlantTabs);
|
ShowTabs(PlantTabs);
|
||||||
|
|
||||||
var text = new StringBuilder();
|
|
||||||
switch (_tab)
|
switch (_tab)
|
||||||
{
|
{
|
||||||
case Tab.Overview:
|
case Tab.Overview:
|
||||||
BuildOverview(text, entity, grow, traits, species);
|
BuildOverview(entity, grow, traits, species);
|
||||||
break;
|
break;
|
||||||
case Tab.Genes:
|
case Tab.Genes:
|
||||||
BuildGenes(text, traits);
|
BuildGenes(traits);
|
||||||
break;
|
break;
|
||||||
case Tab.Products:
|
case Tab.Products:
|
||||||
BuildProducts(text, entity, def, traits);
|
BuildProducts(entity, def, traits);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_body.Text = text.ToString().TrimEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateAnimal(Entity entity)
|
private void UpdateAnimal(Entity entity)
|
||||||
@@ -249,31 +317,27 @@ internal sealed class InspectPanel
|
|||||||
_title.Text = languages.Get(sp.Def.Label);
|
_title.Text = languages.Get(sp.Def.Label);
|
||||||
ShowTabs(AnimalTabs);
|
ShowTabs(AnimalTabs);
|
||||||
|
|
||||||
var text = new StringBuilder();
|
|
||||||
switch (_tab)
|
switch (_tab)
|
||||||
{
|
{
|
||||||
case Tab.Overview:
|
case Tab.Overview:
|
||||||
BuildAnimalOverview(text, entity, org, sp);
|
BuildAnimalOverview(entity, org, sp);
|
||||||
break;
|
break;
|
||||||
case Tab.Needs:
|
case Tab.Needs:
|
||||||
BuildAnimalNeeds(text, entity);
|
BuildAnimalNeeds(entity);
|
||||||
break;
|
break;
|
||||||
case Tab.Skills:
|
case Tab.Skills:
|
||||||
BuildAnimalSkills(text, entity);
|
BuildAnimalSkills(entity);
|
||||||
break;
|
break;
|
||||||
case Tab.Health:
|
case Tab.Health:
|
||||||
BuildAnimalHealth(text, entity);
|
BuildAnimalHealth(entity);
|
||||||
break;
|
break;
|
||||||
case Tab.Mood:
|
case Tab.Mood:
|
||||||
BuildAnimalMood(text, entity);
|
BuildAnimalMood(entity);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_body.Text = text.ToString().TrimEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildOverview(
|
private void BuildOverview(
|
||||||
StringBuilder text,
|
|
||||||
Entity entity,
|
Entity entity,
|
||||||
in PlantGrowth grow,
|
in PlantGrowth grow,
|
||||||
in PlantPhenotype traits,
|
in PlantPhenotype traits,
|
||||||
@@ -284,7 +348,7 @@ internal sealed class InspectPanel
|
|||||||
var lastStage = species.Stages.Length - 1;
|
var lastStage = species.Stages.Length - 1;
|
||||||
var mature = grow.Stage >= lastStage;
|
var mature = grow.Stage >= lastStage;
|
||||||
|
|
||||||
text.AppendLine(
|
Line(
|
||||||
languages.Format(
|
languages.Format(
|
||||||
"inspect.stage",
|
"inspect.stage",
|
||||||
StageLabel(species.Def, grow.Stage),
|
StageLabel(species.Def, grow.Stage),
|
||||||
@@ -294,17 +358,17 @@ internal sealed class InspectPanel
|
|||||||
);
|
);
|
||||||
if (mature)
|
if (mature)
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Get("inspect.mature"));
|
Line(languages.Get("inspect.mature"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var from = species.Stages[grow.Stage].EnterDay;
|
var from = species.Stages[grow.Stage].EnterDay;
|
||||||
var to = species.Stages[grow.Stage + 1].EnterDay;
|
var to = species.Stages[grow.Stage + 1].EnterDay;
|
||||||
var pct = to > from ? (grow.AgeDays - from) / (to - from) * 100f : 0f;
|
var pct = to > from ? (grow.AgeDays - from) / (to - from) : 0f;
|
||||||
text.AppendLine(languages.Format("inspect.growing", Math.Clamp(pct, 0f, 100f)));
|
Bar(languages.Get("inspect.tab.overview"), Math.Clamp(pct, 0f, 1f), Neutral);
|
||||||
}
|
}
|
||||||
|
|
||||||
text.AppendLine(languages.Format("inspect.age", grow.AgeDays, traits.Lifespan));
|
Line(languages.Format("inspect.age", grow.AgeDays, traits.Lifespan));
|
||||||
|
|
||||||
// Вес растения (∝ кубу размера текущей стадии относительно зрелой).
|
// Вес растения (∝ кубу размера текущей стадии относительно зрелой).
|
||||||
var plantMass = Mass.OfPlant(
|
var plantMass = Mass.OfPlant(
|
||||||
@@ -312,7 +376,7 @@ internal sealed class InspectPanel
|
|||||||
species.Stages[grow.Stage].SizeCells,
|
species.Stages[grow.Stage].SizeCells,
|
||||||
species.Stages[^1].SizeCells
|
species.Stages[^1].SizeCells
|
||||||
);
|
);
|
||||||
text.AppendLine(languages.Format("inspect.plantmass", plantMass));
|
Line(languages.Format("inspect.plantmass", plantMass));
|
||||||
|
|
||||||
// Состояние по температуре (рост/покой) и накопленный стресс.
|
// Состояние по температуре (рост/покой) и накопленный стресс.
|
||||||
var (tMin, tLow, tHigh, tMax) = traits.TemperatureBand();
|
var (tMin, tLow, tHigh, tMax) = traits.TemperatureBand();
|
||||||
@@ -324,29 +388,25 @@ internal sealed class InspectPanel
|
|||||||
temperature <= tMin ? "inspect.state.dormantcold" : "inspect.state.dormanthot"
|
temperature <= tMin ? "inspect.state.dormantcold" : "inspect.state.dormanthot"
|
||||||
)
|
)
|
||||||
: languages.Get(mature ? "inspect.state.mature" : "inspect.state.growing");
|
: languages.Get(mature ? "inspect.state.mature" : "inspect.state.growing");
|
||||||
text.AppendLine(languages.Format("inspect.state", state));
|
Line(languages.Format("inspect.state", state));
|
||||||
if (entity.TryGetComponent<TemperatureStress>(out var stress) && stress.Days > 0.05f)
|
if (entity.TryGetComponent<TemperatureStress>(out var stress) && stress.Days > 0.05f)
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Format("inspect.stress", stress.Days));
|
Line(languages.Format("inspect.stress", stress.Days), Warn);
|
||||||
}
|
}
|
||||||
|
|
||||||
text.AppendLine(languages.Format("inspect.temp", tMin, tMax, tLow, tHigh));
|
Line(languages.Format("inspect.temp", tMin, tMax, tLow, tHigh));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildGenes(StringBuilder text, in PlantPhenotype traits)
|
private void BuildGenes(in PlantPhenotype traits)
|
||||||
{
|
{
|
||||||
var languages = _content.Languages;
|
var languages = _content.Languages;
|
||||||
text.AppendLine(languages.Format("inspect.gene.vigor", traits.Vigor, traits.Lifespan));
|
Line(languages.Format("inspect.gene.vigor", traits.Vigor, traits.Lifespan));
|
||||||
text.AppendLine(
|
Line(languages.Format("inspect.gene.env", traits.OptimalLight, traits.OptimalFertility));
|
||||||
languages.Format("inspect.gene.env", traits.OptimalLight, traits.OptimalFertility)
|
Line(
|
||||||
);
|
|
||||||
text.AppendLine(
|
|
||||||
languages.Format("inspect.gene.repro", traits.DispersalRange, traits.ReproduceInterval)
|
languages.Format("inspect.gene.repro", traits.DispersalRange, traits.ReproduceInterval)
|
||||||
);
|
);
|
||||||
text.AppendLine(
|
Line(languages.Format("inspect.gene.repro2", traits.SelfPollination, traits.MutationRate));
|
||||||
languages.Format("inspect.gene.repro2", traits.SelfPollination, traits.MutationRate)
|
Line(
|
||||||
);
|
|
||||||
text.AppendLine(
|
|
||||||
languages.Format(
|
languages.Format(
|
||||||
"inspect.gene.hardy",
|
"inspect.gene.hardy",
|
||||||
traits.ColdHardiness,
|
traits.ColdHardiness,
|
||||||
@@ -356,30 +416,21 @@ internal sealed class InspectPanel
|
|||||||
);
|
);
|
||||||
if (traits.Toxicity > 0.01f || traits.Thorns > 0.01f)
|
if (traits.Toxicity > 0.01f || traits.Thorns > 0.01f)
|
||||||
{
|
{
|
||||||
text.AppendLine(
|
Line(languages.Format("inspect.gene.defense", traits.Toxicity, traits.Thorns), Warn);
|
||||||
languages.Format("inspect.gene.defense", traits.Toxicity, traits.Thorns)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (traits.IsVariant)
|
if (traits.IsVariant)
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Get("inspect.gene.variant"));
|
Line(languages.Get("inspect.gene.variant"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildProducts(
|
private void BuildProducts(Entity entity, PlantDef def, in PlantPhenotype traits)
|
||||||
StringBuilder text,
|
|
||||||
Entity entity,
|
|
||||||
PlantDef def,
|
|
||||||
in PlantPhenotype traits
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
var languages = _content.Languages;
|
var languages = _content.Languages;
|
||||||
if (def.HarvestProduct is { } harvest)
|
if (def.HarvestProduct is { } harvest)
|
||||||
{
|
{
|
||||||
text.AppendLine(
|
Line(languages.Format("inspect.harvest", ProductLabel(harvest), traits.HarvestAmount));
|
||||||
languages.Format("inspect.harvest", ProductLabel(harvest), traits.HarvestAmount)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (def.FruitProduct is { } fruit && traits.FruitYield >= 1f)
|
if (def.FruitProduct is { } fruit && traits.FruitYield >= 1f)
|
||||||
@@ -388,7 +439,7 @@ internal sealed class InspectPanel
|
|||||||
var season = languages.Get(
|
var season = languages.Get(
|
||||||
"season." + ((Season)traits.FruitSeason).ToString().ToLowerInvariant()
|
"season." + ((Season)traits.FruitSeason).ToString().ToLowerInvariant()
|
||||||
);
|
);
|
||||||
text.AppendLine(
|
Line(
|
||||||
languages.Format(
|
languages.Format(
|
||||||
"inspect.fruit",
|
"inspect.fruit",
|
||||||
ProductLabel(fruit),
|
ProductLabel(fruit),
|
||||||
@@ -400,29 +451,22 @@ internal sealed class InspectPanel
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Get("inspect.barren"));
|
Line(languages.Get("inspect.barren"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// — Животное: Обзор — стадия, возраст, пол/поколение, беременность.
|
// — Животное: Обзор — стадия, возраст, пол/поколение, вес, беременность.
|
||||||
private void BuildAnimalOverview(
|
private void BuildAnimalOverview(Entity entity, in AnimalOrganism org, AnimalSet.Species sp)
|
||||||
StringBuilder text,
|
|
||||||
Entity entity,
|
|
||||||
in AnimalOrganism org,
|
|
||||||
AnimalSet.Species sp
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
var languages = _content.Languages;
|
var languages = _content.Languages;
|
||||||
var grow = entity.GetComponent<AnimalGrowth>();
|
var grow = entity.GetComponent<AnimalGrowth>();
|
||||||
var stageName = languages.Get(
|
var stageName = languages.Get(
|
||||||
"animalstage." + sp.Stages[grow.Stage].Name.ToLowerInvariant()
|
"animalstage." + sp.Stages[grow.Stage].Name.ToLowerInvariant()
|
||||||
);
|
);
|
||||||
text.AppendLine(
|
Line(languages.Format("inspect.stage", stageName, grow.Stage + 1, sp.Stages.Length));
|
||||||
languages.Format("inspect.stage", stageName, grow.Stage + 1, sp.Stages.Length)
|
Line(languages.Format("inspect.age", grow.AgeDays, org.Traits.Lifespan));
|
||||||
);
|
|
||||||
text.AppendLine(languages.Format("inspect.age", grow.AgeDays, org.Traits.Lifespan));
|
|
||||||
var sex = languages.Get(org.IsMale ? "inspect.sex.male" : "inspect.sex.female");
|
var sex = languages.Get(org.IsMale ? "inspect.sex.male" : "inspect.sex.female");
|
||||||
text.AppendLine(languages.Format("inspect.animal.sex", sex, org.Generation));
|
Line(languages.Format("inspect.animal.sex", sex, org.Generation));
|
||||||
|
|
||||||
// Вес тела и грузоподъёмность (∝ массе × способность нести; четвероногие — через Moving).
|
// Вес тела и грузоподъёмность (∝ массе × способность нести; четвероногие — через Moving).
|
||||||
var stageScale = sp.Stages[grow.Stage].Scale;
|
var stageScale = sp.Stages[grow.Stage].Scale;
|
||||||
@@ -431,16 +475,16 @@ internal sealed class InspectPanel
|
|||||||
entity.TryGetComponent<Health>(out var health) && health.State is not null
|
entity.TryGetComponent<Health>(out var health) && health.State is not null
|
||||||
? Mass.CarryCapacity(mass, health.State.Capacity(AnimalCapacities.Moving))
|
? Mass.CarryCapacity(mass, health.State.Capacity(AnimalCapacities.Moving))
|
||||||
: Mass.CarryCapacity(mass, 1f);
|
: Mass.CarryCapacity(mass, 1f);
|
||||||
text.AppendLine(languages.Format("inspect.mass", mass, carryCap));
|
Line(languages.Format("inspect.mass", mass, carryCap));
|
||||||
|
|
||||||
if (entity.TryGetComponent<Pregnant>(out var preg))
|
if (entity.TryGetComponent<Pregnant>(out var preg))
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Format("inspect.animal.pregnant", preg.DueInDays));
|
Line(languages.Format("inspect.animal.pregnant", preg.DueInDays), Ui.Accent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// — Животное: Нужды — значение каждой нужды (голод/жажда/отдых/влечение) в процентах.
|
// — Животное: Нужды — полоса на каждую нужду; убывающие красят «хорошо↔плохо», влечения — нейтрально.
|
||||||
private void BuildAnimalNeeds(StringBuilder text, Entity entity)
|
private void BuildAnimalNeeds(Entity entity)
|
||||||
{
|
{
|
||||||
var languages = _content.Languages;
|
var languages = _content.Languages;
|
||||||
var values = entity.GetComponent<AnimalNeeds>().Values;
|
var values = entity.GetComponent<AnimalNeeds>().Values;
|
||||||
@@ -451,23 +495,19 @@ internal sealed class InspectPanel
|
|||||||
|
|
||||||
for (var k = 0; k < _needs.Count && k < values.Length; k++)
|
for (var k = 0; k < _needs.Count && k < values.Length; k++)
|
||||||
{
|
{
|
||||||
text.AppendLine(
|
var v = values[k];
|
||||||
languages.Format(
|
var color = _needs.IsDrive(k) ? Neutral : GoodBad(v);
|
||||||
"inspect.needline",
|
Bar(languages.Get(_needs[k].Label), v, color);
|
||||||
languages.Get(_needs[k].Label),
|
|
||||||
values[k] * 100f
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// — Животное: Навыки — уровень каждого навыка (%) и страсть (★/★★), растут от практики.
|
// — Животное: Навыки — полоса уровня (нейтральный цвет) + страсть (★/★★) в подписи.
|
||||||
private void BuildAnimalSkills(StringBuilder text, Entity entity)
|
private void BuildAnimalSkills(Entity entity)
|
||||||
{
|
{
|
||||||
var languages = _content.Languages;
|
var languages = _content.Languages;
|
||||||
if (!entity.TryGetComponent<Skills>(out var sk) || sk.Levels is null)
|
if (!entity.TryGetComponent<Skills>(out var sk) || sk.Levels is null)
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Get("inspect.health.none"));
|
Line(languages.Get("inspect.health.none"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,45 +517,41 @@ internal sealed class InspectPanel
|
|||||||
sk.Passions is not null && i < sk.Passions.Length ? sk.Passions[i] : (byte)0;
|
sk.Passions is not null && i < sk.Passions.Length ? sk.Passions[i] : (byte)0;
|
||||||
var stars = passion switch
|
var stars = passion switch
|
||||||
{
|
{
|
||||||
2 => "★★",
|
2 => " ★★",
|
||||||
1 => "★",
|
1 => " ★",
|
||||||
_ => "",
|
_ => "",
|
||||||
};
|
};
|
||||||
text.AppendLine(
|
Bar(languages.Get(_skills[i].Label) + stars, sk.Levels[i], Neutral);
|
||||||
languages.Format(
|
|
||||||
"inspect.skillline",
|
|
||||||
languages.Get(_skills[i].Label),
|
|
||||||
sk.Levels[i] * 100f,
|
|
||||||
stars
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// — Животное: Здоровье — кровь/боль, ключевые способности, активные хедифы (раны/болезни/гон).
|
// — Животное: Здоровье — полосы жизненных показателей, способностей и активных состояний.
|
||||||
private void BuildAnimalHealth(StringBuilder text, Entity entity)
|
private void BuildAnimalHealth(Entity entity)
|
||||||
{
|
{
|
||||||
var languages = _content.Languages;
|
var languages = _content.Languages;
|
||||||
var state = entity.GetComponent<Health>().State;
|
var state = entity.GetComponent<Health>().State;
|
||||||
if (state is null)
|
if (state is null)
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Get("inspect.health.none"));
|
Line(languages.Get("inspect.health.none"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
text.AppendLine(
|
Header("inspect.health.vitals");
|
||||||
languages.Format("inspect.health.blood", state.BloodLevel * 100f, state.Pain * 100f)
|
Bar(languages.Get("inspect.vital.blood"), state.BloodLevel, GoodBad(state.BloodLevel));
|
||||||
);
|
// Боль: длина = уровень боли, цвет инвертирован (много боли — красный).
|
||||||
|
Bar(languages.Get("inspect.vital.pain"), state.Pain, GoodBad(1f - state.Pain));
|
||||||
|
|
||||||
if (state.Capacities.Count > 0)
|
if (state.Capacities.Count > 0)
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Get("inspect.health.caps"));
|
Header("inspect.health.caps");
|
||||||
// Сначала известные способности в порядке отображения, затем прочие (модовые) — как есть.
|
// Сначала известные способности в порядке отображения, затем прочие (модовые) — как есть.
|
||||||
var shown = new HashSet<string>(StringComparer.Ordinal);
|
var shown = new HashSet<string>(StringComparer.Ordinal);
|
||||||
foreach (var capId in AnimalCapacities.DisplayOrder)
|
foreach (var capId in AnimalCapacities.DisplayOrder)
|
||||||
{
|
{
|
||||||
if (state.Capacities.ContainsKey(capId))
|
if (state.Capacities.ContainsKey(capId))
|
||||||
{
|
{
|
||||||
AppendCapacity(text, state, capId, CapacityLabel(capId));
|
var v = state.Capacity(capId);
|
||||||
|
Bar(languages.Get(CapacityLabel(capId)), v, GoodBad(v));
|
||||||
shown.Add(capId);
|
shown.Add(capId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,82 +560,65 @@ internal sealed class InspectPanel
|
|||||||
{
|
{
|
||||||
if (shown.Add(capId))
|
if (shown.Add(capId))
|
||||||
{
|
{
|
||||||
AppendCapacity(text, state, capId, CapacityLabel(capId));
|
var v = state.Capacity(capId);
|
||||||
|
Bar(languages.Get(CapacityLabel(capId)), v, GoodBad(v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Header("inspect.health.hediffs");
|
||||||
if (state.Hediffs.Count > 0)
|
if (state.Hediffs.Count > 0)
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Get("inspect.health.hediffs"));
|
|
||||||
foreach (var hediff in state.Hediffs)
|
foreach (var hediff in state.Hediffs)
|
||||||
{
|
{
|
||||||
text.AppendLine(
|
Bar(
|
||||||
languages.Format(
|
languages.Get(hediff.Def.Label),
|
||||||
"inspect.health.hediffline",
|
hediff.Severity,
|
||||||
languages.Get(hediff.Def.Label),
|
GoodBad(1f - hediff.Severity)
|
||||||
hediff.Severity * 100f
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Get("inspect.health.none"));
|
Line(languages.Get("inspect.health.none"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ключ локализации метки способности: cap.<id в нижнем регистре> (cap.consciousness, cap.bloodpumping…).
|
// Ключ локализации метки способности: cap.<id в нижнем регистре> (cap.consciousness, cap.bloodpumping…).
|
||||||
private static string CapacityLabel(string capId) => "cap." + capId.ToLowerInvariant();
|
private static string CapacityLabel(string capId) => "cap." + capId.ToLowerInvariant();
|
||||||
|
|
||||||
private void AppendCapacity(
|
// — Животное: Настроение — полоса значения и активные событийные мысли с их сдвигом (тир 4+).
|
||||||
StringBuilder text,
|
private void BuildAnimalMood(Entity entity)
|
||||||
HealthState state,
|
|
||||||
string capId,
|
|
||||||
string labelKey
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var languages = _content.Languages;
|
|
||||||
text.AppendLine(
|
|
||||||
languages.Format(
|
|
||||||
"inspect.health.cap",
|
|
||||||
languages.Get(labelKey),
|
|
||||||
state.Capacity(capId) * 100f
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// — Животное: Настроение — значение и активные событийные мысли с их сдвигом (тир 4+).
|
|
||||||
private void BuildAnimalMood(StringBuilder text, Entity entity)
|
|
||||||
{
|
{
|
||||||
var languages = _content.Languages;
|
var languages = _content.Languages;
|
||||||
var mood = entity.GetComponent<Mood>();
|
var mood = entity.GetComponent<Mood>();
|
||||||
if (!mood.Active)
|
if (!mood.Active)
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Get("inspect.mood.none"));
|
Line(languages.Get("inspect.mood.none"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
text.AppendLine(languages.Format("inspect.mood.value", mood.Value * 100f));
|
Bar(languages.Get("inspect.tab.mood"), mood.Value, GoodBad(mood.Value));
|
||||||
if (mood.Thoughts is { Count: > 0 })
|
if (mood.Thoughts is { Count: > 0 })
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Get("inspect.mood.thoughts"));
|
Header("inspect.mood.thoughts");
|
||||||
foreach (var thought in mood.Thoughts)
|
foreach (var thought in mood.Thoughts)
|
||||||
{
|
{
|
||||||
var pct = thought.Def.MoodOffset * 100f;
|
var pct = thought.Def.MoodOffset * 100f;
|
||||||
var offset = (pct >= 0f ? "+" : "") + pct.ToString("0") + "%";
|
var offset = (pct >= 0f ? "+" : "") + pct.ToString("0") + "%";
|
||||||
text.AppendLine(
|
Line(
|
||||||
languages.Format(
|
languages.Format(
|
||||||
"inspect.mood.thoughtline",
|
"inspect.mood.thoughtline",
|
||||||
languages.Get(thought.Def.Label),
|
languages.Get(thought.Def.Label),
|
||||||
offset
|
offset
|
||||||
)
|
),
|
||||||
|
pct >= 0f ? Body : Warn
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
text.AppendLine(languages.Get("inspect.mood.calm"));
|
Line(languages.Get("inspect.mood.calm"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,3 +632,62 @@ internal sealed class InspectPanel
|
|||||||
? _content.Languages.Get(product.Label)
|
? _content.Languages.Get(product.Label)
|
||||||
: productDefName;
|
: productDefName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Полоса-индикатор показателя в стиле RimWorld: тёмная дорожка фиксированной ширины, цветная заливка
|
||||||
|
/// длиной <c>fraction</c>, поверх — подпись слева и значение справа. Самодостаточный виджет (ничего не
|
||||||
|
/// обновляет — пересоздаётся при каждой пересборке тела панели), цвет заливки задаёт вызывающий.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class StatBar : Panel
|
||||||
|
{
|
||||||
|
private static readonly IBrush Track = new SolidBrush(new Color(28, 34, 44));
|
||||||
|
private static readonly IBrush TrackBorder = new SolidBrush(new Color(64, 78, 98));
|
||||||
|
private const int BarHeight = 22;
|
||||||
|
|
||||||
|
public StatBar(int width, string name, float fraction, string value, Color fill)
|
||||||
|
{
|
||||||
|
Width = width;
|
||||||
|
Height = BarHeight;
|
||||||
|
Background = Track;
|
||||||
|
Border = TrackBorder;
|
||||||
|
BorderThickness = new Thickness(1);
|
||||||
|
|
||||||
|
var fillWidth = (int)MathF.Round((width - 2) * Math.Clamp(fraction, 0f, 1f));
|
||||||
|
if (fillWidth > 0)
|
||||||
|
{
|
||||||
|
Widgets.Add(
|
||||||
|
new Panel
|
||||||
|
{
|
||||||
|
Background = new SolidBrush(fill),
|
||||||
|
Width = fillWidth,
|
||||||
|
Height = BarHeight - 2,
|
||||||
|
Left = 1,
|
||||||
|
Top = 1,
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = VerticalAlignment.Top,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.Add(
|
||||||
|
new Label
|
||||||
|
{
|
||||||
|
Text = name,
|
||||||
|
TextColor = Color.White,
|
||||||
|
Left = 8,
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Widgets.Add(
|
||||||
|
new Label
|
||||||
|
{
|
||||||
|
Text = value,
|
||||||
|
TextColor = Color.White,
|
||||||
|
Margin = new Thickness(0, 0, 8, 0),
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Right,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user