Ремонтник и лутер: зона из 3x3 квадратов, лишние выключаются (#84)
Сетка «Где работать» в «Содержимом» урны, квадраты очерчены линиями при показе границ; выключенные квадраты обход пропускает. Сохраняется пятым числом v6 (старая DLL его пропустит). Консоль: necrourn x y z sectors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
9a567b6ac0
commit
96d930e5bb
+74
-11
@@ -264,8 +264,9 @@ namespace NecromancerTome
|
||||
AppendVia(via, l);
|
||||
}
|
||||
CinerariumWriteHelper.WriteInts(_bw, via.ToArray());
|
||||
// v6: ремонтник/лутер - сторона зоны, идёт ли проход, где курсор обхода, кто нажал кнопку.
|
||||
CinerariumWriteHelper.WriteInts(_bw, new[] { ZoneSide, ZoneRunning ? 1 : 0, ZoneCursor, ZonePressedBy });
|
||||
// v6: ремонтник/лутер - сторона зоны, идёт ли проход, где курсор обхода, кто нажал кнопку; пятое (28.10, #84) -
|
||||
// выключенные квадраты зоны. Числа идут со счётчиком - старая DLL пятое просто пропустит, версия та же.
|
||||
CinerariumWriteHelper.WriteInts(_bw, new[] { ZoneSide, ZoneRunning ? 1 : 0, ZoneCursor, ZonePressedBy, ZoneOff });
|
||||
}
|
||||
|
||||
static void AppendVia(List<int> _dst, List<Vector3i> _l)
|
||||
@@ -411,6 +412,7 @@ namespace NecromancerTome
|
||||
ZoneRunning = false;
|
||||
ZoneCursor = 0;
|
||||
ZonePressedBy = -1;
|
||||
ZoneOff = 0;
|
||||
if (version >= 6)
|
||||
{
|
||||
int n = _br.ReadInt32();
|
||||
@@ -423,6 +425,7 @@ namespace NecromancerTome
|
||||
ZoneRunning = n > 1 && v[1] != 0;
|
||||
ZoneCursor = n > 2 ? v[2] : 0;
|
||||
ZonePressedBy = n > 3 ? v[3] : -1;
|
||||
ZoneOff = n > 4 ? v[4] : 0;
|
||||
}
|
||||
_threadsDirty = true;
|
||||
if (version > SaveVersion)
|
||||
@@ -1099,19 +1102,11 @@ namespace NecromancerTome
|
||||
}
|
||||
_bounds = new GameObject("NecroCinerariumBounds");
|
||||
_bounds.transform.SetParent(t, false);
|
||||
LineRenderer lr = _bounds.AddComponent<LineRenderer>();
|
||||
lr.useWorldSpace = false;
|
||||
lr.widthMultiplier = 0.06f;
|
||||
if (_boundsMat == null)
|
||||
{
|
||||
_boundsMat = new Material(Shader.Find("Sprites/Default")) { name = "NecroCinerariumBounds" };
|
||||
}
|
||||
lr.sharedMaterial = _boundsMat;
|
||||
Color col = new Color(0.55f, 0.9f, 0.75f, 0.85f);
|
||||
lr.startColor = col;
|
||||
lr.endColor = col;
|
||||
lr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
lr.receiveShadows = false;
|
||||
LineRenderer lr = BoundsLine(_bounds, BoundsOn);
|
||||
|
||||
// Углы по граням блоков: x и z от -2.5 до +2.5 от центра урны, y от низа блока под ней до верха +4. Большая зона
|
||||
// (ремонтник, лутер) - сторона ZoneSideNow, по высоте весь столб мира.
|
||||
@@ -1129,6 +1124,74 @@ namespace NecromancerTome
|
||||
{
|
||||
lr.SetPosition(i, t.InverseTransformPoint(path[i]));
|
||||
}
|
||||
if (IsBigZone)
|
||||
{
|
||||
SpawnSectorLines(t, c, x0, z0, y0, y1);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Color BoundsOn = new Color(0.55f, 0.9f, 0.75f, 0.85f);
|
||||
static readonly Color BoundsOff = new Color(0.95f, 0.3f, 0.25f, 0.85f);
|
||||
|
||||
/// <summary>Ломаная границ - дочерний объект _parent (уходит с ним в RemoveBounds), материал общий.</summary>
|
||||
static LineRenderer BoundsLine(GameObject _parent, Color _col)
|
||||
{
|
||||
GameObject go = _parent;
|
||||
if (go.GetComponent<LineRenderer>() != null)
|
||||
{
|
||||
go = new GameObject("NecroCinerariumBoundsLine");
|
||||
go.transform.SetParent(_parent.transform, false);
|
||||
}
|
||||
LineRenderer lr = go.AddComponent<LineRenderer>();
|
||||
lr.useWorldSpace = false;
|
||||
lr.widthMultiplier = 0.06f;
|
||||
lr.sharedMaterial = _boundsMat;
|
||||
lr.startColor = _col;
|
||||
lr.endColor = _col;
|
||||
lr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
lr.receiveShadows = false;
|
||||
return lr;
|
||||
}
|
||||
|
||||
/// <summary>Квадраты большой зоны (#84): на уровне пола урны - рамка каждого квадрата (включён - зелёная, выключен - красная
|
||||
/// с крестом), чуть отступив внутрь, чтобы соседние не сливались; на границах квадратов - вертикали на всю высоту зоны.</summary>
|
||||
void SpawnSectorLines(Transform _t, Vector3 _c, float _x0, float _z0, float _y0, float _y1)
|
||||
{
|
||||
int side = ZoneSideNow;
|
||||
float y = _c.y + 0.05f, inset = 0.2f;
|
||||
float Edge(float _from, int _k) => _from + BandStart(_k, side);
|
||||
for (int sz = 0; sz < ZoneGrid; sz++)
|
||||
{
|
||||
for (int sx = 0; sx < ZoneGrid; sx++)
|
||||
{
|
||||
bool on = SectorOn(sx + sz * ZoneGrid);
|
||||
float ax = Edge(_x0, sx) + inset, bx = Edge(_x0, sx + 1) - inset;
|
||||
float az = Edge(_z0, sz) + inset, bz = Edge(_z0, sz + 1) - inset;
|
||||
Vector3 p0 = new Vector3(ax, y, az), p1 = new Vector3(bx, y, az), p2 = new Vector3(bx, y, bz), p3 = new Vector3(ax, y, bz);
|
||||
Vector3[] path = on ? new[] { p0, p1, p2, p3, p0 } : new[] { p0, p1, p2, p3, p0, p2, p1, p3 };
|
||||
LineRenderer lr = BoundsLine(_bounds, on ? BoundsOn : BoundsOff);
|
||||
lr.positionCount = path.Length;
|
||||
for (int i = 0; i < path.Length; i++)
|
||||
{
|
||||
lr.SetPosition(i, _t.InverseTransformPoint(path[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Вертикали - во всех узлах сетки, кроме углов зоны (их уже рисует рамка).
|
||||
for (int kz = 0; kz <= ZoneGrid; kz++)
|
||||
{
|
||||
for (int kx = 0; kx <= ZoneGrid; kx++)
|
||||
{
|
||||
if ((kx == 0 || kx == ZoneGrid) && (kz == 0 || kz == ZoneGrid))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
LineRenderer lr = BoundsLine(_bounds, BoundsOn);
|
||||
lr.positionCount = 2;
|
||||
lr.SetPosition(0, _t.InverseTransformPoint(new Vector3(Edge(_x0, kx), _y0, Edge(_z0, kz))));
|
||||
lr.SetPosition(1, _t.InverseTransformPoint(new Vector3(Edge(_x0, kx), _y1, Edge(_z0, kz))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveBounds()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace NecromancerTome
|
||||
{
|
||||
/// <summary>
|
||||
/// Квадраты зоны ремонтника и лутера (28.10.2026, #84): сетка 3x3 в окне «Содержимое» - окно necroUrnZone в группе окон
|
||||
/// игры bagStorage (Config/XUi_InGame), как фильтр логиста. Север сверху, в среднем квадрате - урна. Щелчок включает /
|
||||
/// выключает квадрат (TEFeatureCinerarium.ToggleSector); выключенный - тёмный с красной рамкой. Видна только у урн с
|
||||
/// большой зоной; у прочих урн и сумок транспорта и волка скрыта. s0..s8 в XML - номера квадратов (столбец + строка * 3,
|
||||
/// строка 0 - юг), f0..f8 - их рамки.
|
||||
/// </summary>
|
||||
[Preserve]
|
||||
public class XUiC_NecroUrnZone : XUiController
|
||||
{
|
||||
readonly XUiController[] _cells = new XUiController[TEFeatureCinerarium.ZoneSectors];
|
||||
readonly XUiController[] _frames = new XUiController[TEFeatureCinerarium.ZoneSectors];
|
||||
static readonly Color On = new Color(0.3f, 0.6f, 0.45f, 0.9f);
|
||||
static readonly Color Off = new Color(0.12f, 0.12f, 0.12f, 0.9f);
|
||||
static readonly Color FrameOn = new Color(0.55f, 0.9f, 0.75f, 1f);
|
||||
static readonly Color FrameOff = new Color(0.95f, 0.3f, 0.25f, 1f);
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
for (int i = 0; i < _cells.Length; i++)
|
||||
{
|
||||
int s = i;
|
||||
_cells[i] = GetChildById("s" + i);
|
||||
_frames[i] = GetChildById("f" + i);
|
||||
if (_cells[i] != null)
|
||||
{
|
||||
_cells[i].OnPress += (_x, _m) => Toggle(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOpen()
|
||||
{
|
||||
base.OnOpen();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void Toggle(int _s)
|
||||
{
|
||||
TEFeatureCinerarium urn = CinerariumContents.Urn;
|
||||
if (urn == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
urn.ToggleSector(_s);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void Refresh()
|
||||
{
|
||||
TEFeatureCinerarium urn = CinerariumContents.Urn;
|
||||
bool show = urn != null && urn.IsBigZone;
|
||||
ViewComponent.IsVisible = show;
|
||||
if (!show)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < _cells.Length; i++)
|
||||
{
|
||||
bool on = urn.SectorOn(i);
|
||||
if (_cells[i]?.ViewComponent is XUiV_Sprite sp)
|
||||
{
|
||||
sp.Color = on ? On : Off;
|
||||
sp.ToolTip = Localization.Get(on ? "necroZoneSectorOn" : "necroZoneSectorOff");
|
||||
}
|
||||
if (_frames[i]?.ViewComponent is XUiV_Sprite fr)
|
||||
{
|
||||
fr.Color = on ? FrameOn : FrameOff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace NecromancerTome
|
||||
/// necroslot [N] - какая ячейка пояса в руке и что в ячейках; N - взять в руку ячейку N (23.10: проверка Archer's Eye без мыши)
|
||||
/// necrofire press|release - ЛКМ предметом в руке: нажать / отпустить (лук: натянуть / выстрелить; 23.10, ошибка в Hit от стрелы)
|
||||
/// necrobox x y z list|clear|add предмет число - содержимое хранилища (ящик, сумка урны)
|
||||
/// necrourn x y z info|skull 0|1|from|to|out bx by bz|clear|sample предмет число|group все|нет - урна
|
||||
/// necrourn x y z info|skull 0|1|from|to|out bx by bz|clear|sample предмет число|group все|нет|sectors all|N|-маска - урна
|
||||
/// necroclick 0|1 - отпущенная ЛКМ/ПКМ ножом для нитей по блоку под прицелом
|
||||
/// necropossess near [класс]|fp|hit|look id / x y z|slot N|belt N предмет [качество]|info|exit - одержимость без мыши (#37, 25.10)
|
||||
/// </summary>
|
||||
@@ -552,6 +552,16 @@ namespace NecromancerTome
|
||||
case "run": u.ZoneCommand("zurn_run", GameManager.Instance.World.GetPrimaryPlayer()); break;
|
||||
case "stop": u.ZoneCommand("zurn_stop", null); break;
|
||||
case "size": u.ZoneSide = int.Parse(_p[4]); u.SetModified(); break;
|
||||
// necrourn x y z sectors all|N|-N - квадраты зоны (#84): all - все включить; N - переключить квадрат N (0..8, столбец +
|
||||
// строка * 3, 0 - юго-запад); -N - маска выключенных числом.
|
||||
case "sectors":
|
||||
if (_p.Count > 4)
|
||||
{
|
||||
if (_p[4] == "all" || _p[4] == "все") u.SetZoneOff(0);
|
||||
else if (_p[4].StartsWith("-")) u.SetZoneOff(int.Parse(_p[4].Substring(1)));
|
||||
else u.ToggleSector(int.Parse(_p[4]));
|
||||
}
|
||||
break;
|
||||
case "group":
|
||||
u.GroupMask = _p[4] == "нет" || _p[4] == "none" ? 0u : CinerariumGroups.All;
|
||||
u.SetModified();
|
||||
@@ -567,7 +577,7 @@ namespace NecromancerTome
|
||||
}
|
||||
Out("necrourn: " + pos + " " + _w.GetBlock(pos).Block?.GetBlockName() + ", череп " + u.HasSkull + ", застрял " + u.Stuck + ", мигание " + blink +
|
||||
", откуда " + u.ThreadFrom.Count + ", куда " + (u.HasThreadTo ? u.ThreadTo.ToString() : "нет") + ", от урны " + u.ThreadOut.Count +
|
||||
", зона " + (u.IsBigZone ? u.ZoneSideNow + (u.ZoneRunning ? " идёт" : " ждёт") + (u.ZoneLacking ? " нехватка" : "") + " курсор " + u.ZoneCursor : "-") + ", образец " + (u.Sample == null || u.Sample.IsEmpty() ? "нет" : u.Sample.itemValue.ItemClass?.GetItemName() + "x" + u.Sample.count) + "; " + u.WorkState());
|
||||
", зона " + (u.IsBigZone ? u.ZoneSideNow + (u.ZoneRunning ? " идёт" : " ждёт") + (u.ZoneLacking ? " нехватка" : "") + " курсор " + u.ZoneCursor + " квадраты " + u.SectorsText() : "-") + ", образец " + (u.Sample == null || u.Sample.IsEmpty() ? "нет" : u.Sample.itemValue.ItemClass?.GetItemName() + "x" + u.Sample.count) + "; " + u.WorkState());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user