2026-10-12 23:54:03 +03:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.UI;
|
|
|
|
|
|
using TMPro;
|
|
|
|
|
|
|
|
|
|
|
|
// ВОЙСКА: армия, очередь обучения (идёт и когда игра закрыта), найм воинов за ресурсы,
|
2026-11-01 23:34:08 +03:00
|
|
|
|
// ускорение за кристаллы. Найм -- карточки войск в ряд (листаются вбок): иконка, сила, цена,
|
|
|
|
|
|
// против кого силён и слаб (из правил боя). Новые типы войск открывают уровни Ратуши и Казарм.
|
2026-10-12 23:54:03 +03:00
|
|
|
|
public class TroopsPanel : BasePanel
|
|
|
|
|
|
{
|
|
|
|
|
|
readonly List<Image> queueBars = new List<Image>();
|
|
|
|
|
|
readonly List<TextMeshProUGUI> queueTimes = new List<TextMeshProUGUI>();
|
|
|
|
|
|
Button speedUp;
|
|
|
|
|
|
|
|
|
|
|
|
static readonly int[] Amounts = { 1, 10, 50 };
|
|
|
|
|
|
|
2026-11-05 17:05:59 +01:00
|
|
|
|
int shownBarracks; // сколько Казарм было, когда окно рисовалось: достроились -- перерисовать
|
|
|
|
|
|
TextMeshProUGUI barracksBuildingLeft; // «Осталось: 0:45», пока Казармы строятся
|
|
|
|
|
|
|
|
|
|
|
|
// Казармы, которые ещё строятся: сколько секунд до ближайшего конца; -1 -- таких нет
|
|
|
|
|
|
static double BarracksConstructionSecondsLeft()
|
|
|
|
|
|
{
|
|
|
|
|
|
double best = -1;
|
|
|
|
|
|
foreach (var t in BuildTimer.All)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (t == null || !t.IsConstruction) continue;
|
|
|
|
|
|
var id = t.GetComponent<PlacedBuildingId>();
|
|
|
|
|
|
if (id == null || id.buildingName != BarracksBootstrap.BarracksName) continue;
|
|
|
|
|
|
if (best < 0 || t.SecondsLeft < best) best = t.SecondsLeft;
|
|
|
|
|
|
}
|
|
|
|
|
|
return best;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-12 23:54:03 +03:00
|
|
|
|
protected override void Build(RectTransform root)
|
|
|
|
|
|
{
|
|
|
|
|
|
var d = GP.Data;
|
|
|
|
|
|
queueBars.Clear(); queueTimes.Clear(); speedUp = null;
|
|
|
|
|
|
var list = ScrollList(root);
|
|
|
|
|
|
|
|
|
|
|
|
// --- Армия ---
|
|
|
|
|
|
var army = UIKit.Card(list, UIKit.Theme.CardDark);
|
|
|
|
|
|
ProfilePanel.Header(army, "Армия");
|
|
|
|
|
|
ProfilePanel.KV(army, "Всего воинов в городе", GP.TotalTroops.ToString("N0"));
|
2026-10-23 18:33:04 +02:00
|
|
|
|
ProfilePanel.KV(army, "В походе на карте мира", GameServices.Get<IArmy>().TroopsInMarches.ToString("N0"));
|
2026-10-28 22:11:36 +03:00
|
|
|
|
if (GameServices.TryGet(out IPopulation population))
|
|
|
|
|
|
{
|
2026-10-31 19:34:33 +03:00
|
|
|
|
// Воины берутся из свободных жителей (модуль Населения), резерв не нанять
|
2026-10-28 22:11:36 +03:00
|
|
|
|
ProfilePanel.KV(army, "Обучаются в Казармах", population.InTraining.ToString("N0"));
|
|
|
|
|
|
ProfilePanel.KV(army, "Можно нанять из жителей", population.Recruitable.ToString("N0") +
|
|
|
|
|
|
" (резерв " + population.CivilianReserve.ToString("N0") + ")");
|
|
|
|
|
|
}
|
2026-11-01 13:32:25 +01:00
|
|
|
|
// Раненые (решения 31.10, п. 7): лечатся в госпитале или ждут места; не воюют, но считаются людьми
|
|
|
|
|
|
IArmy hurt = GameServices.Get<IArmy>();
|
|
|
|
|
|
if (hurt.WoundedTotal > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
int waiting = 0, healingNow = 0;
|
|
|
|
|
|
long readyMs = long.MaxValue;
|
|
|
|
|
|
for (int t = 0; t < hurt.TypeCount; t++) waiting += hurt.GetWoundedWaiting(t);
|
|
|
|
|
|
foreach (var h in hurt.Healing) { healingNow += h.Count; readyMs = System.Math.Min(readyMs, h.EndMs); }
|
|
|
|
|
|
string next = readyMs != long.MaxValue
|
|
|
|
|
|
? ", ближайшие вылечатся через " + GameData.FormatTime(System.Math.Max(0, readyMs - GameClock.UnixNowMs) / 1000.0)
|
|
|
|
|
|
: "";
|
|
|
|
|
|
ProfilePanel.KV(army, "Раненые", hurt.WoundedTotal.ToString("N0") + " (лечатся " + healingNow + ", ждут места " + waiting + ")");
|
|
|
|
|
|
ProfilePanel.KV(army, "Госпиталь", "мест " + hurt.HospitalBeds + next);
|
|
|
|
|
|
}
|
2026-11-04 15:16:43 +01:00
|
|
|
|
if (hurt.MissingTotal > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
long windowEnd = 0;
|
|
|
|
|
|
foreach (var m in hurt.Missing) windowEnd = System.Math.Max(windowEnd, m.EndMs);
|
|
|
|
|
|
ProfilePanel.KV(army, "Пропали без вести", hurt.MissingTotal.ToString("N0") + " (могут вернуться ещё " +
|
|
|
|
|
|
GameData.FormatTime(System.Math.Max(0, windowEnd - GameClock.UnixNowMs) / 1000.0) + ")");
|
|
|
|
|
|
}
|
2026-10-12 23:54:03 +03:00
|
|
|
|
ProfilePanel.KV(army, "Сила войск", GP.TroopsPower.ToString("N0"));
|
|
|
|
|
|
ProfilePanel.KV(army, "Сила армии с героем", GP.ArmyPower.ToString("N0"));
|
|
|
|
|
|
UIKit.Button(army, "Отправить армию на карте мира", UIKit.Theme.Green, () => OpenPanel("World"), -1, 80, 26);
|
|
|
|
|
|
|
2026-10-27 16:54:49 +01:00
|
|
|
|
// --- Гарнизон: часть воинов охраняет город и в походы не уходит ---
|
|
|
|
|
|
IArmy troopsArmy = GameServices.Get<IArmy>();
|
|
|
|
|
|
var garrison = UIKit.Card(list, UIKit.Theme.CardDark);
|
|
|
|
|
|
ProfilePanel.Header(garrison, "Гарнизон города");
|
|
|
|
|
|
ProfilePanel.KV(garrison, "В гарнизоне", troopsArmy.GarrisonTotal.ToString("N0") + " из " + troopsArmy.SoldiersAtHome.ToString("N0"));
|
|
|
|
|
|
ProfilePanel.KV(garrison, "Свободны для походов", troopsArmy.AvailableForMarch.ToString("N0"));
|
|
|
|
|
|
UIKit.Text(garrison, "Гарнизон защищает город и не уходит в походы. Воины в нём не пропадают: всего воинов столько же.", 22, UIKit.Theme.TextDim);
|
|
|
|
|
|
var gRow = UIKit.Row(garrison, 80, 10);
|
|
|
|
|
|
GarrisonButton(gRow, troopsArmy, "0", 0);
|
|
|
|
|
|
GarrisonButton(gRow, troopsArmy, "-10", troopsArmy.GarrisonTotal - 10);
|
|
|
|
|
|
GarrisonButton(gRow, troopsArmy, "-1", troopsArmy.GarrisonTotal - 1);
|
|
|
|
|
|
GarrisonButton(gRow, troopsArmy, "+1", troopsArmy.GarrisonTotal + 1);
|
|
|
|
|
|
GarrisonButton(gRow, troopsArmy, "+10", troopsArmy.GarrisonTotal + 10);
|
|
|
|
|
|
GarrisonButton(gRow, troopsArmy, "Все", troopsArmy.SoldiersAtHome);
|
|
|
|
|
|
|
2026-10-12 23:54:03 +03:00
|
|
|
|
// --- Нет казарм -- нанимать негде ---
|
2026-11-05 17:05:59 +01:00
|
|
|
|
shownBarracks = Barracks.Count;
|
|
|
|
|
|
barracksBuildingLeft = null;
|
2026-10-12 23:54:03 +03:00
|
|
|
|
if (Barracks.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
var warn = UIKit.Card(list, new Color(0.35f, 0.18f, 0.12f));
|
2026-11-05 17:05:59 +01:00
|
|
|
|
double left = BarracksConstructionSecondsLeft();
|
|
|
|
|
|
if (left >= 0)
|
2026-10-12 23:54:03 +03:00
|
|
|
|
{
|
2026-11-05 17:05:59 +01:00
|
|
|
|
// Казармы поставлены, но ещё строятся (05.11: в 3D их было не отличить от готовых)
|
|
|
|
|
|
UIKit.Text(warn, "Казармы строятся. Когда стройка закончится, здесь можно будет нанимать войска.", 28, UIKit.Theme.Text);
|
|
|
|
|
|
barracksBuildingLeft = UIKit.Text(warn, "Осталось: " + GameData.FormatTime(left), 28, UIKit.Theme.Text);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
UIKit.Text(warn, "Нанимать войска можно только в Казармах. Постройте их на свободном участке.", 28, UIKit.Theme.Text);
|
|
|
|
|
|
UIKit.Button(warn, "ПОСТРОИТЬ КАЗАРМЫ", UIKit.Theme.Green, () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (City3DController.Instance != null) City3DController.Instance.PlaceBuilding("Казармы"); // 3D-город
|
|
|
|
|
|
else if (BuildModeController.Instance != null) BuildModeController.Instance.SetMode(true);
|
|
|
|
|
|
else UIKit.Toast("Нажмите кнопку СТРОЙКА");
|
|
|
|
|
|
}, -1, 96, 32);
|
|
|
|
|
|
}
|
2026-10-12 23:54:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- Очередь обучения ---
|
|
|
|
|
|
var queue = UIKit.Card(list);
|
2026-11-01 13:19:34 +01:00
|
|
|
|
var training = GP.Training;
|
|
|
|
|
|
ProfilePanel.Header(queue, "Обучение (" + training.Count + " / " + Barracks.QueueSize + ") • казарм: " + Barracks.Count);
|
|
|
|
|
|
if (training.Count == 0)
|
2026-10-12 23:54:03 +03:00
|
|
|
|
{
|
|
|
|
|
|
UIKit.Text(queue, "Никто не обучается. Выберите войска ниже.", 26, UIKit.Theme.TextDim);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-11-01 13:19:34 +01:00
|
|
|
|
foreach (var o in training)
|
2026-10-12 23:54:03 +03:00
|
|
|
|
{
|
2026-11-01 13:19:34 +01:00
|
|
|
|
var def = GameData.Troops[o.Type];
|
2026-10-12 23:54:03 +03:00
|
|
|
|
var r = UIKit.Row(queue, 44, 10);
|
2026-11-01 13:19:34 +01:00
|
|
|
|
var n = UIKit.Text(r, def.name + " × " + o.Count, 28);
|
2026-10-12 23:54:03 +03:00
|
|
|
|
UIKit.Size(n, -1, -1, 1);
|
|
|
|
|
|
var t = UIKit.Text(r, "", 26, UIKit.Theme.Gold, TextAlignmentOptions.Right);
|
|
|
|
|
|
UIKit.Size(t, 180);
|
|
|
|
|
|
queueTimes.Add(t);
|
|
|
|
|
|
queueBars.Add(UIKit.Bar(queue, 0, def.color, 20));
|
|
|
|
|
|
}
|
|
|
|
|
|
speedUp = UIKit.Button(queue, "", UIKit.Theme.Blue, () => GP.SpeedUpTraining(), -1, 80);
|
|
|
|
|
|
}
|
|
|
|
|
|
Tick();
|
|
|
|
|
|
|
2026-11-01 23:34:08 +03:00
|
|
|
|
// --- Типы войск: карточки в ряд, листаются вбок ---
|
|
|
|
|
|
var troopsCard = UIKit.Card(list, UIKit.Theme.CardDark);
|
|
|
|
|
|
ProfilePanel.Header(troopsCard, "Найм войск");
|
|
|
|
|
|
UIKit.Text(troopsCard, "Листайте карточки вбок →", 22, UIKit.Theme.TextDim);
|
|
|
|
|
|
var strip = HorizontalStrip(troopsCard, CardHeight);
|
|
|
|
|
|
for (int i = 0; i < GameData.Troops.Count; i++) TroopCard(strip, i);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------- Карточка войска ----------------
|
|
|
|
|
|
// Место под иконку: пока квадрат цвета войска с первой буквой. Картинку поставим в TroopIcon, когда будет арт.
|
|
|
|
|
|
// "Силён / слаб против" берётся из правил боя (контры в модуле Боя и BattleConfig), а не пишется руками:
|
|
|
|
|
|
// поменяют контры -- поменяется и текст.
|
|
|
|
|
|
|
|
|
|
|
|
const float CardWidth = 340f;
|
|
|
|
|
|
const float CardHeight = 640f;
|
|
|
|
|
|
|
|
|
|
|
|
void TroopCard(Transform strip, int i)
|
|
|
|
|
|
{
|
|
|
|
|
|
int idx = i;
|
|
|
|
|
|
var def = GameData.Troops[i];
|
|
|
|
|
|
bool unlocked = GP.TroopUnlocked(i);
|
|
|
|
|
|
|
|
|
|
|
|
var card = UIKit.Card(strip, unlocked ? UIKit.Theme.Card : UIKit.Theme.CardDark, 16, 8);
|
|
|
|
|
|
UIKit.Size(card, CardWidth, -1);
|
|
|
|
|
|
|
|
|
|
|
|
var iconRow = UIKit.Row(card, 130, 0);
|
|
|
|
|
|
var lpad = UIKit.Make("Pad", iconRow); UIKit.Size(lpad, -1, -1, 1);
|
|
|
|
|
|
TroopIcon(iconRow, i, unlocked);
|
|
|
|
|
|
var rpad = UIKit.Make("Pad", iconRow); UIKit.Size(rpad, -1, -1, 1);
|
|
|
|
|
|
|
|
|
|
|
|
UIKit.Text(card, def.name, 32, UIKit.Theme.Text, TextAlignmentOptions.Center).fontStyle = FontStyles.Bold;
|
|
|
|
|
|
UIKit.Text(card, "в армии: " + GP.TroopCount(i).ToString("N0"), 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
|
|
|
|
|
UIKit.Text(card, "Сила " + def.power + " • " + def.secondsPerUnit + " сек/воин", 22, UIKit.Theme.Text, TextAlignmentOptions.Center);
|
|
|
|
|
|
UIKit.Text(card, GameData.RewardsText(def.cost), 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
|
|
|
|
|
|
|
|
|
|
|
string strong, weak;
|
|
|
|
|
|
CounterText(i, out strong, out weak);
|
|
|
|
|
|
if (strong.Length > 0) UIKit.Text(card, "<color=#9be59b>Силён против:</color> " + strong, 22, UIKit.Theme.Text);
|
|
|
|
|
|
if (weak.Length > 0) UIKit.Text(card, "<color=#ff8a80>Слаб против:</color> " + weak, 22, UIKit.Theme.Text);
|
|
|
|
|
|
UIKit.Text(card, def.description, 20, UIKit.Theme.TextDim);
|
|
|
|
|
|
|
|
|
|
|
|
var fill = UIKit.Make("Fill", card); UIKit.Size(fill, -1, -1).flexibleHeight = 1;
|
|
|
|
|
|
|
|
|
|
|
|
if (!unlocked)
|
2026-10-12 23:54:03 +03:00
|
|
|
|
{
|
2026-11-01 23:34:08 +03:00
|
|
|
|
UIKit.Text(card, "Откроется:\n" + GP.TroopLockText(i), 24, UIKit.Theme.Gold, TextAlignmentOptions.Center);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-10-12 23:54:03 +03:00
|
|
|
|
|
2026-11-01 23:34:08 +03:00
|
|
|
|
var btns = UIKit.Row(card, 70, 8);
|
|
|
|
|
|
foreach (int amount in Amounts)
|
|
|
|
|
|
{
|
|
|
|
|
|
int a = amount;
|
|
|
|
|
|
bool can = Barracks.Count > 0 && GP.CanAfford(def.cost, a) && GP.Training.Count < Barracks.QueueSize && GP.Recruitable >= a;
|
|
|
|
|
|
var b = UIKit.Button(btns, "+" + a, can ? UIKit.Theme.Green : UIKit.Theme.Gray, () => GP.Train(idx, a), -1, 70, 26);
|
|
|
|
|
|
UIKit.Size(b, -1, 70, 1);
|
2026-10-12 23:54:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-01 23:34:08 +03:00
|
|
|
|
// Иконка войска. Сейчас -- заглушка (цветной квадрат с буквой); сюда же потом подставим спрайт.
|
|
|
|
|
|
static void TroopIcon(Transform parent, int i, bool unlocked)
|
|
|
|
|
|
{
|
|
|
|
|
|
var def = GameData.Troops[i];
|
|
|
|
|
|
UIKit.Icon(parent, unlocked ? def.color : UIKit.Theme.Gray, def.name.Substring(0, 1), 120);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static readonly string[] TroopNamesGenitive = { "пехоты", "лучников", "кавалерии", "осадных" };
|
|
|
|
|
|
|
|
|
|
|
|
// Контры из правил боя: против кого этот тип сильнее и кто сильнее против него
|
|
|
|
|
|
static void CounterText(int type, out string strong, out string weak)
|
|
|
|
|
|
{
|
|
|
|
|
|
var s = new List<string>();
|
|
|
|
|
|
var w = new List<string>();
|
|
|
|
|
|
var battle = GameServices.TryGet(out IBattleSystem b) ? b as BattleSystem : null;
|
|
|
|
|
|
if (battle != null)
|
|
|
|
|
|
for (int j = 0; j < TroopType.Count && j < GameData.Troops.Count; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (j == type) continue;
|
|
|
|
|
|
int mine = battle.Counter(type, j), theirs = battle.Counter(j, type);
|
|
|
|
|
|
if (mine > 0) s.Add(Genitive(j) + " (+" + mine + "% силы)");
|
|
|
|
|
|
if (theirs > 0) w.Add(Genitive(j));
|
|
|
|
|
|
}
|
|
|
|
|
|
if (type == TroopType.Siege)
|
|
|
|
|
|
{
|
|
|
|
|
|
var cfg = BattleConfig.Load();
|
|
|
|
|
|
if (cfg.siegeVsWallsPercent > 0) s.Add("стен при штурме (+" + cfg.siegeVsWallsPercent + "%)");
|
|
|
|
|
|
if (cfg.siegeInFieldPercent < 0) w.Add("всех в поле и в обороне (" + cfg.siegeInFieldPercent + "%)");
|
|
|
|
|
|
}
|
|
|
|
|
|
strong = string.Join(", ", s.ToArray());
|
|
|
|
|
|
weak = string.Join(", ", w.ToArray());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static string Genitive(int type)
|
|
|
|
|
|
{
|
|
|
|
|
|
return type >= 0 && type < TroopNamesGenitive.Length ? TroopNamesGenitive[type] : GameData.Troops[type].name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Горизонтальная лента с прокруткой вбок внутри общего вертикального списка.
|
|
|
|
|
|
// Вертикальный свайп по ленте прокручивает весь список (NestedScrollRect).
|
|
|
|
|
|
static RectTransform HorizontalStrip(Transform parent, float height)
|
|
|
|
|
|
{
|
|
|
|
|
|
var root = UIKit.Make("TroopStrip", parent);
|
|
|
|
|
|
UIKit.Size(root, -1, height);
|
|
|
|
|
|
root.gameObject.AddComponent<Image>().color = new Color(0, 0, 0, 0);
|
|
|
|
|
|
var scroll = root.gameObject.AddComponent<NestedScrollRect>();
|
|
|
|
|
|
scroll.horizontal = true;
|
|
|
|
|
|
scroll.vertical = false;
|
|
|
|
|
|
scroll.movementType = ScrollRect.MovementType.Elastic;
|
|
|
|
|
|
scroll.scrollSensitivity = 40f;
|
|
|
|
|
|
|
|
|
|
|
|
var viewport = UIKit.Stretch(UIKit.Make("Viewport", root));
|
|
|
|
|
|
viewport.gameObject.AddComponent<RectMask2D>();
|
|
|
|
|
|
|
|
|
|
|
|
var content = UIKit.Make("Content", viewport);
|
|
|
|
|
|
content.anchorMin = new Vector2(0, 0); content.anchorMax = new Vector2(0, 1);
|
|
|
|
|
|
content.pivot = new Vector2(0, 0.5f);
|
|
|
|
|
|
content.offsetMin = Vector2.zero; content.offsetMax = Vector2.zero;
|
|
|
|
|
|
var h = content.gameObject.AddComponent<HorizontalLayoutGroup>();
|
|
|
|
|
|
h.spacing = 14;
|
|
|
|
|
|
h.padding = new RectOffset(4, 4, 4, 4);
|
|
|
|
|
|
h.childControlWidth = true; h.childControlHeight = true;
|
|
|
|
|
|
h.childForceExpandWidth = false; h.childForceExpandHeight = true;
|
|
|
|
|
|
content.gameObject.AddComponent<ContentSizeFitter>().horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
|
|
|
|
|
|
|
|
|
|
scroll.viewport = viewport;
|
|
|
|
|
|
scroll.content = content;
|
|
|
|
|
|
return content;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-27 16:54:49 +01:00
|
|
|
|
// Окно перестраивается само: Армия публикует TroopsChangedEvent, GameProgress сообщает об изменении
|
|
|
|
|
|
static void GarrisonButton(Transform row, IArmy army, string label, int target)
|
|
|
|
|
|
{
|
|
|
|
|
|
target = Mathf.Clamp(target, 0, army.SoldiersAtHome);
|
|
|
|
|
|
bool can = target != army.GarrisonTotal;
|
|
|
|
|
|
var b = UIKit.Button(row, label, can ? UIKit.Theme.Blue : UIKit.Theme.Gray, () => { if (can) army.SetGarrison(target); }, -1, 80, 28);
|
|
|
|
|
|
UIKit.Size(b, -1, 80, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-12 23:54:03 +03:00
|
|
|
|
protected override void Tick()
|
|
|
|
|
|
{
|
2026-11-05 17:05:59 +01:00
|
|
|
|
if (Barracks.Count != shownBarracks) { RequestRebuild(); return; } // Казармы достроились (или снесены)
|
|
|
|
|
|
if (barracksBuildingLeft != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
double left = BarracksConstructionSecondsLeft();
|
|
|
|
|
|
barracksBuildingLeft.text = "Осталось: " + GameData.FormatTime(System.Math.Max(0, left));
|
|
|
|
|
|
}
|
2026-11-01 13:19:34 +01:00
|
|
|
|
var training = GP.Training;
|
|
|
|
|
|
for (int i = 0; i < queueBars.Count && i < training.Count; i++)
|
2026-10-12 23:54:03 +03:00
|
|
|
|
{
|
2026-11-01 13:19:34 +01:00
|
|
|
|
var o = training[i];
|
2026-10-12 23:54:03 +03:00
|
|
|
|
UIKit.SetBar(queueBars[i], GP.TrainingProgress(o));
|
|
|
|
|
|
queueTimes[i].text = GameData.FormatTime(GP.TrainingSecondsLeft(o));
|
|
|
|
|
|
}
|
2026-11-01 13:19:34 +01:00
|
|
|
|
if (speedUp != null && training.Count > 0)
|
2026-10-12 23:54:03 +03:00
|
|
|
|
UIKit.SetButton(speedUp, "Ускорить всё за " + GP.SpeedUpCost + " кристаллов", true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|