Разведка М3 (ядро): тайные операции -- группа до 10 пехоты/лучников, до 2 операций, проверки обнаружения, прерывание и возврат

- Разведка: CheckCovertGroup, StartCovert, CheckCovertUntil (15/25/35%, при входе и каждые 5 реальных минут, максимум при пересечении, 5-75%, детерминированно по одной сетке), EndCovert; сохранение covert=
- Армия: AvailableUnits, TakeUnitsForMarch -- поход точного состава
- Мир: AbortMission -- домой от места обнаружения, без отчёта; охраняемые области и позиция армии через IWorldMap
- Карта: «Тайно» в окне атаки и окне чужой армии, окно «РАЗВЕДЧИКОВ ЗАМЕТИЛИ»
- «Шпион» -- переключатель IntelligenceConfig (ТЕСТ); 5 тестов

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Alexander
2026-11-06 17:15:22 +01:00
co-authored by Claude Opus 5.5
parent 56a5a82a8c
commit 2835c514d8
18 changed files with 622 additions and 14 deletions
@@ -156,6 +156,41 @@ namespace Bogatyri.World
progress = 0;
}
// ---------------- Разведка М3: миссия прервана ----------------
// Тайный отряд обнаружен в момент t: миссия прервана (без отчёта и боя), отряд физически идёт домой от места,
// где был в этот момент. Только для своих разведок, которые ещё не идут домой
public bool AbortMission(MarchData m, long t)
{
if (m == null || !m.scout || m.aborted || !marches.Contains(m)) return false;
if (m.state != MarchState.Outbound && m.state != MarchState.Gathering) return false;
if (!TryGetPositionAt(m, t, out int x, out int y, out _, out _, out _)) return false;
MapNode home = GetNode(m.fromNodeId);
if (home == null) return false;
int[] units = HasUnits(m) ? m.units : null;
m.aborted = true;
m.abortMs = t;
m.leaveX = x; m.leaveY = y;
m.targetEmpty = true; // отчёта не будет
m.victory = false;
m.survivors = m.soldiers;
m.survivorUnits = units != null ? (int[])units.Clone() : null;
m.loot = new System.Collections.Generic.List<RewardEntry>();
m.state = MarchState.Returning;
m.returnMs = t + TravelTimeMs(Point(x, y), home, units);
return true;
}
bool TryGetAbortedPositionAt(MarchData m, long t, out int x, out int y, out int toX, out int toY, out int soldiers)
{
x = y = toX = toY = 0;
soldiers = m.soldiers;
MapNode home = GetNode(m.fromNodeId);
if (home == null || m.returnMs > 0 && t >= m.returnMs) return false; // дома
LerpXY(m.leaveX, m.leaveY, home.x, home.y, m.abortMs, m.returnMs, t, out x, out y);
toX = home.x; toY = home.y;
return true;
}
static MapNode Point(int x, int y) => new MapNode { id = "", x = x, y = y, displayName = "" };
static void LerpXY(int ax, int ay, int bx, int by, long start, long end, long t, out int x, out int y)