Ремонтник и лутер: зона из 3x3 квадратов, лишние выключаются (#84)

Сетка «Где работать» в «Содержимом» урны, квадраты очерчены линиями при
показе границ; выключенные квадраты обход пропускает. Сохраняется пятым
числом v6 (старая DLL его пропустит). Консоль: necrourn x y z sectors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-28 10:21:39 +03:00
co-authored by Claude Opus 5.5
parent 9a567b6ac0
commit 96d930e5bb
9 changed files with 302 additions and 17 deletions
+98 -2
View File
@@ -8,7 +8,8 @@ namespace NecromancerTome
/// Урны с большой зоной - ремонтник и лутер (решения 17.10 и 18.10, BACKLOG «Урна ремонтника», «Урна лутера»).
///
/// Общее. Зона - квадрат со стороной блока притязания (LandClaimSize сервера, по умолчанию 41) или меньше (31/21/11, пункт
/// меню «Размер зоны»), по высоте - весь столб мира; зоны таких урн могут пересекаться. Работа - только по кнопке (17.10: «А
/// меню «Размер зоны»), по высоте - весь столб мира; делится на 3x3 квадрата, любой можно выключить (#84, ZoneOff); зоны
/// таких урн могут пересекаться. Работа - только по кнопке (17.10: «А
/// если сделать рубильник или кнопку. Включил, ремонтник начал работу»): «Запустить» - один проход по зоне, конец прохода -
/// снова ожидание; до нажатия урна ничего не считает. Кнопки - на самой урне с четырёх сторон («как у дверного звонка, но с
/// черепом»), их цвет - состояние (CinerariumButtons): не горят - нет черепа; жёлтая ровно - ожидание; жёлтая мигает - не
@@ -123,6 +124,88 @@ namespace NecromancerTome
}
}
/// <summary>Квадраты зоны (28.10, #84, пользователь: «разделить уже имеющуюся зону на 9 квадратов, с цинерарием посередине...
/// по умолчанию все 9 были выбраны, но можно было какой-нибудь отключить. Иначе он начинает ремонтировать близстоящие
/// здания»): сетка ZoneGrid x ZoneGrid, урна в среднем квадрате. Номер квадрата - столбец + строка * ZoneGrid, столбец 0 -
/// запад (меньший x), строка 0 - юг (меньший z). Бит в ZoneOff - квадрат выключен; 0 - все включены. Сохраняется (v6,
/// пятое число). Выключать - в «Содержимом» (XUiC_NecroUrnZone) или «necrourn x y z sectors».</summary>
public int ZoneOff;
public const int ZoneGrid = 3;
public const int ZoneSectors = ZoneGrid * ZoneGrid;
public bool SectorOn(int _s) => (ZoneOff & (1 << _s)) == 0;
/// <summary>Полоса сетки клетки _i (0..side-1, от западного/южного края зоны).</summary>
static int Band(int _i, int _side) => _i * ZoneGrid / _side;
/// <summary>Первая клетка полосы _k от края зоны; _k = ZoneGrid - сторона зоны.</summary>
public static int BandStart(int _k, int _side) => (_k * _side + ZoneGrid - 1) / ZoneGrid;
/// <summary>Работает ли урна в столбе мира (x, z): в зоне и квадрат включён.</summary>
public bool InZone(int _x, int _z)
{
int side = ZoneSideNow, half = side / 2;
Vector3i c = ToWorldPos();
int ix = _x - c.x + half, iz = _z - c.z + half;
if (ix < 0 || iz < 0 || ix >= side || iz >= side)
{
return false;
}
return SectorOn(Band(ix, side) + Band(iz, side) * ZoneGrid);
}
/// <summary>Включить/выключить квадрат. Блоки выключенного квадрата, уже взятые в очередь, отпускаются; линии перерисовать.</summary>
public void SetZoneOff(int _mask)
{
ZoneOff = _mask & ((1 << ZoneSectors) - 1);
DropOutside(_queue);
DropOutside(_lacking);
if (_zonePending && IsRepairer && !InZone(_zoneTarget.x, _zoneTarget.z))
{
_zonePending = false;
Claimed.Remove(_zoneTarget);
}
if (ZoneShown)
{
RemoveBounds();
SpawnBounds();
}
SetModified();
Debug.Log("[NecromancerTome] Cinerarium: урна " + ToWorldPos() + " - квадраты зоны " + SectorsText());
}
public void ToggleSector(int _s) => SetZoneOff(ZoneOff ^ (1 << _s));
/// <summary>Квадраты строками с севера: «+» включён, «-» выключен (для лога и консоли).</summary>
public string SectorsText()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int r = ZoneGrid - 1; r >= 0; r--)
{
for (int col = 0; col < ZoneGrid; col++)
{
sb.Append(SectorOn(col + r * ZoneGrid) ? '+' : '-');
}
if (r > 0)
{
sb.Append('/');
}
}
return sb.ToString();
}
void DropOutside(List<Vector3i> _l)
{
for (int i = _l.Count - 1; i >= 0; i--)
{
if (!InZone(_l[i].x, _l[i].z))
{
Claimed.Remove(_l[i]);
_l.RemoveAt(i);
}
}
}
public enum ButtonState
{
Off,
@@ -317,6 +400,10 @@ namespace NecromancerTome
{
int x = c.x - half + ZoneCursor % side, z = c.z - half + ZoneCursor / side;
ZoneCursor++;
if (ZoneOff != 0 && !InZone(x, z))
{
continue; // квадрат выключен (#84)
}
if (!(_world.GetChunkFromWorldPos(x, 0, z) is Chunk ch))
{
continue; // не загружен - пропустить столб
@@ -585,7 +672,16 @@ namespace NecromancerTome
foreach (Entity e in _ents)
{
int rank = LootRank(e);
if (rank < bestRank && !_lootDone.Contains(e.entityId) && !LockManager.Instance.IsLockedServer(e, 0))
if (rank >= bestRank)
{
continue;
}
Vector3i ep = World.worldToBlockPos(e.GetPosition());
if (ZoneOff != 0 && !InZone(ep.x, ep.z))
{
continue; // квадрат выключен (#84)
}
if (!_lootDone.Contains(e.entityId) && !LockManager.Instance.IsLockedServer(e, 0))
{
best = e;
bestRank = rank;