2026-11-04 15:21:54 +01:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using TMPro;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
|
|
// Отчёты разведки на экране (Разведка М2): текст отчёта и окно «Отчёты» с последними отчётами.
|
|
|
|
|
|
// Сами отчёты хранит модуль Разведки (IIntelligence.Reports) -- здесь только показ.
|
|
|
|
|
|
// Сведения -- на момент отчёта: сколько прошло с тех пор, видно в каждом отчёте.
|
|
|
|
|
|
public static class ReconReportText
|
|
|
|
|
|
{
|
|
|
|
|
|
static readonly string[] TroopNames = { "пехота", "лучники", "кавалерия", "осадные" };
|
|
|
|
|
|
static readonly Dictionary<string, string> ResourceNames = new Dictionary<string, string>
|
|
|
|
|
|
{
|
|
|
|
|
|
{ "Food", "еда" }, { "Wood", "дерево" }, { "Stone", "камень" }, { "Iron", "металл" }, { "Gold", "золото" }, { "Gems", "кристаллы" }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
public static string Format(ReconReport r, long nowMs)
|
|
|
|
|
|
{
|
|
|
|
|
|
var lines = new List<string>
|
|
|
|
|
|
{
|
|
|
|
|
|
$"<b>{r.TargetName}</b> — разведка {(r.Level == 3 ? "III" : "II")}, {Ago(nowMs - r.TimeMs)} назад",
|
|
|
|
|
|
};
|
|
|
|
|
|
int total = 0;
|
|
|
|
|
|
var parts = new List<string>();
|
|
|
|
|
|
for (int i = 0; r.Units != null && i < r.Units.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
total += r.Units[i];
|
|
|
|
|
|
if (r.Units[i] > 0) parts.Add($"{TroopNames[i % TroopNames.Length]} {r.Units[i]}");
|
|
|
|
|
|
}
|
|
|
|
|
|
lines.Add($"Гарнизон: {total}" + (parts.Count > 0 ? $" ({string.Join(", ", parts)})" : ""));
|
|
|
|
|
|
lines.Add((r.Fortified ? "Защитники за стенами" : "Стен нет, бой в поле") + (r.HasHero ? ", есть герой" : ", героя нет"));
|
|
|
|
|
|
if (r.Level >= 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!string.IsNullOrEmpty(r.Modifiers)) lines.Add("Защита: " + r.Modifiers);
|
|
|
|
|
|
var res = new List<string>();
|
|
|
|
|
|
for (int i = 0; r.ResourceNames != null && i < r.ResourceNames.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
string name = ResourceNames.TryGetValue(r.ResourceNames[i], out var n) ? n : r.ResourceNames[i];
|
|
|
|
|
|
res.Add($"{name} ~{r.ResourceMin[i]}–{r.ResourceMax[i]}");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (res.Count > 0) lines.Add("Ресурсы (оценка ±15%): " + string.Join(", ", res));
|
|
|
|
|
|
}
|
|
|
|
|
|
return string.Join("\n", lines);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static string Ago(long ms)
|
|
|
|
|
|
{
|
|
|
|
|
|
long min = System.Math.Max(0, ms) / 60_000;
|
|
|
|
|
|
if (min < 1) return "только что";
|
|
|
|
|
|
return min < 60 ? $"{min} мин" : $"{min / 60} ч {min % 60} мин";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-06 16:55:59 +01:00
|
|
|
|
// Окно чужой армии (Разведка М2): что видно сейчас, последний отчёт и «Разведка II / III» -- отряд догонит армию,
|
|
|
|
|
|
// пойдёт рядом время наблюдения и вернётся с отчётом. Воинов -- до ArmyReconSoldiers из свободных
|
|
|
|
|
|
public const int ArmyReconSoldiers = 10;
|
|
|
|
|
|
|
|
|
|
|
|
public static void OpenArmyWindow(KnownContact army, System.Action<string> hint)
|
|
|
|
|
|
{
|
|
|
|
|
|
var card = UIKit.Modal("ЧУЖАЯ АРМИЯ", out GameObject root);
|
|
|
|
|
|
long now = MarchManager.NowMs;
|
|
|
|
|
|
string owner = army.Identified ? IntelligenceOverlay.OwnerName(MarchManager.Instance.World, army.OwnerId) : "владелец неизвестен";
|
|
|
|
|
|
UIKit.Text(card, $"~{army.ApproxMin}–{army.ApproxMax} воинов, {owner}", 26, UIKit.Theme.Text, TextAlignmentOptions.Center);
|
|
|
|
|
|
if (GameServices.TryGet(out IIntelligence intel) && intel.TryGetLatestReport(army.ArmyId, out var report))
|
|
|
|
|
|
UIKit.Text(card, Format(report, now), 22, UIKit.Theme.TextDim, TextAlignmentOptions.Left);
|
|
|
|
|
|
|
|
|
|
|
|
int free = GameServices.TryGet(out IArmy myArmy) ? myArmy.AvailableForMarch : 0;
|
|
|
|
|
|
int soldiers = Mathf.Min(ArmyReconSoldiers, free);
|
|
|
|
|
|
UIKit.Text(card, soldiers > 0 ? $"Отправить {soldiers} воинов: догонят армию, пойдут рядом и вернутся с отчётом. Без боя."
|
|
|
|
|
|
: "Нет свободных воинов для разведки", 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
|
|
|
|
|
for (int level = 2; level <= 3; level++)
|
|
|
|
|
|
{
|
|
|
|
|
|
int lv = level;
|
|
|
|
|
|
string name = lv == 3 ? "III" : "II";
|
|
|
|
|
|
bool ok = MarchManager.Instance.TryPlanArmyRecon(army.ArmyId, lv, out long meet, out long reportAt, out long home, out string error);
|
|
|
|
|
|
string plan = ok ? $"догонят через {Ago(meet - now)}, отчёт через {Ago(reportAt - now)}, дома через {Ago(home - now)}" : error;
|
|
|
|
|
|
UIKit.Text(card, $"Разведка {name}: {plan}", 22, ok ? UIKit.Theme.Text : UIKit.Theme.Gold, TextAlignmentOptions.Center);
|
|
|
|
|
|
var b = UIKit.Button(card, $"Разведка {name}", ok && soldiers > 0 ? UIKit.Theme.Blue : UIKit.Theme.Gray, () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
bool started = MarchManager.Instance.TryStartArmyRecon(army.ArmyId, soldiers, lv, out string err);
|
|
|
|
|
|
hint?.Invoke(started ? $"Разведка {name}: {soldiers} воинов идут догонять армию" : "Разведка не начата: " + err);
|
|
|
|
|
|
Object.Destroy(root);
|
|
|
|
|
|
}, -1, 80, 26);
|
|
|
|
|
|
b.interactable = ok && soldiers > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
UIKit.Button(card, "Закрыть", UIKit.Theme.Gray, () => Object.Destroy(root), -1, 80, 28);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-04 15:21:54 +01:00
|
|
|
|
// Окно «Отчёты»: последние отчёты, новые сверху
|
|
|
|
|
|
public static void OpenWindow()
|
|
|
|
|
|
{
|
|
|
|
|
|
var card = UIKit.Modal("ОТЧЁТЫ РАЗВЕДКИ", out GameObject root);
|
|
|
|
|
|
if (!GameServices.TryGet(out IIntelligence intel) || intel.Reports.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
UIKit.Text(card, "Отчётов пока нет. Нажмите на город, лагерь или замок и отправьте «Разведку II» или «Разведку III».",
|
|
|
|
|
|
26, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
long now = MarchManager.NowMs;
|
|
|
|
|
|
int shown = 0;
|
|
|
|
|
|
for (int i = intel.Reports.Count - 1; i >= 0 && shown < 5; i--, shown++)
|
|
|
|
|
|
UIKit.Text(card, Format(intel.Reports[i], now), 24, UIKit.Theme.Text, TextAlignmentOptions.Left);
|
|
|
|
|
|
if (intel.Reports.Count > 5)
|
|
|
|
|
|
UIKit.Text(card, $"И ещё {intel.Reports.Count - 5} более старых", 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
|
|
|
|
|
}
|
|
|
|
|
|
UIKit.Button(card, "Закрыть", UIKit.Theme.Gray, () => Object.Destroy(root), -1, 80, 28);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|