Урны: череп-ретранслятор, прилив прячет духов, меры нагрузки; проверки без мыши
- Череп-ретранслятор: нить урны проходит через черепа-декор (цепочка), каждый пролёт до 15 м; черепа хранятся у нити (запись урны v5, старые - без черепов). ПКМ ножом по черепу - провести нить, ЛКМ - снять нити через него; убрали череп - нить снимается. - Прилив: ночью кровавой луны духи всех урн исчезают, работа стоит, утром - сама. - Нагрузка: общий бюджет дел духов на кадр (3), анимируются только 16 ближайших духов; лимит урн сборщика смягчён - одна на 200 убийств (было 500), тексты на 13 языках. - Описание логиста (кадило, два вида, маленький дух, мигание), черепа-декора (передаёт нить), подсказки ножа - на 13 языках. - Разработка: necrogod, necrofps [uncap], necrotide, necroshot серией, necroclick по клетке, necrobox fill, necrourn с черепами на пути. Проверено в игре (test8): мигание, нить 28 м через череп и её разрыв, прилив, 63 урны сборщика: 13.0 мс на кадр против 11.8 мс без работы урн. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6693d96d5e
commit
85d8036e81
+231
-38
@@ -68,8 +68,10 @@ namespace NecromancerTome
|
||||
public const int HalfWidth = 2;
|
||||
public const int Below = 1;
|
||||
public const int Above = 4;
|
||||
public const float KillsPerUrn = 500f;
|
||||
const byte SaveVersion = 4; // 2 - нити, 3 - фильтр логиста (16.10), 4 - нити рабочего «куда» и образец (18.10)
|
||||
/// <summary>Убийств на одну урну сборщика. 17.10: «лимит урн - ослабить/убрать» - нагрузку теперь держит общий бюджет
|
||||
/// шагов (CinerariumLoad), лимит остался мягким игровым порогом: было 500, стало 200.</summary>
|
||||
public const float KillsPerUrn = 200f;
|
||||
const byte SaveVersion = 5; // 2 - нити, 3 - фильтр логиста (16.10), 4 - нити рабочего «куда» и образец (18.10), 5 - черепа-ретрансляторы (18.10)
|
||||
|
||||
public bool HasSkull;
|
||||
public bool ZoneShown;
|
||||
@@ -80,6 +82,13 @@ namespace NecromancerTome
|
||||
public bool HasThreads => HasThreadTo || ThreadFrom.Count > 0 || ThreadOut.Count > 0;
|
||||
/// <summary>Нити рабочего «от урны»: станции (где изготавливать) и ящики (куда класть готовое), главные клетки блоков.</summary>
|
||||
public readonly List<Vector3i> ThreadOut = new List<Vector3i>();
|
||||
/// <summary>Черепа-ретрансляторы на пути каждой нити (18.10: «сам череп повысить из простого предмета, до промежуточного
|
||||
/// ретранслятора... если понадобится протащить коммуникации дальше»; «Цепочка из нескольких»): клетки черепов-декора по
|
||||
/// порядку ОТ УРНЫ к концу нити. Списки идут вровень с ThreadFrom / ThreadOut, ToVia - у нити «куда». Пусто - нить прямая.
|
||||
/// Каждый пролёт - не длиннее CinerariumThreads.MaxLength. Убрали череп - нить через него снимается.</summary>
|
||||
public readonly List<List<Vector3i>> FromVia = new List<List<Vector3i>>();
|
||||
public readonly List<List<Vector3i>> OutVia = new List<List<Vector3i>>();
|
||||
public readonly List<Vector3i> ToVia = new List<Vector3i>();
|
||||
/// <summary>Образец рабочего: что изготавливать; число в стопке - сколько держать в запасе (ящики «куда» и выход станций).
|
||||
/// Лежит в «Содержимом» урны второй ячейкой.</summary>
|
||||
public ItemStack Sample = ItemStack.Empty.Clone();
|
||||
@@ -138,6 +147,10 @@ namespace NecromancerTome
|
||||
ThreadTo = other.ThreadTo;
|
||||
ThreadOut.Clear();
|
||||
ThreadOut.AddRange(other.ThreadOut);
|
||||
CopyVia(other.FromVia, FromVia);
|
||||
CopyVia(other.OutVia, OutVia);
|
||||
ToVia.Clear();
|
||||
ToVia.AddRange(other.ToVia);
|
||||
Sample = other.Sample.Clone();
|
||||
GroupMask = other.GroupMask;
|
||||
_threadsDirty = true;
|
||||
@@ -227,6 +240,61 @@ namespace NecromancerTome
|
||||
}
|
||||
CinerariumWriteHelper.WriteInts(_bw, o.ToArray());
|
||||
(Sample ?? ItemStack.Empty).Clone().Write(_bw);
|
||||
// v5: черепа на пути нитей - по нити: число черепов и их клетки; порядок нитей - как выше (откуда, куда, от урны).
|
||||
List<int> via = new List<int>();
|
||||
AlignVia();
|
||||
foreach (List<Vector3i> l in FromVia)
|
||||
{
|
||||
AppendVia(via, l);
|
||||
}
|
||||
AppendVia(via, ToVia);
|
||||
foreach (List<Vector3i> l in OutVia)
|
||||
{
|
||||
AppendVia(via, l);
|
||||
}
|
||||
CinerariumWriteHelper.WriteInts(_bw, via.ToArray());
|
||||
}
|
||||
|
||||
static void AppendVia(List<int> _dst, List<Vector3i> _l)
|
||||
{
|
||||
_dst.Add(_l.Count);
|
||||
foreach (Vector3i p in _l)
|
||||
{
|
||||
_dst.Add(p.x); _dst.Add(p.y); _dst.Add(p.z);
|
||||
}
|
||||
}
|
||||
|
||||
static List<Vector3i> TakeVia(int[] _v, ref int _k)
|
||||
{
|
||||
List<Vector3i> l = new List<Vector3i>();
|
||||
int cnt = _k < _v.Length ? _v[_k++] : 0;
|
||||
for (int i = 0; i < cnt && _k + 2 < _v.Length; i++, _k += 3)
|
||||
{
|
||||
l.Add(new Vector3i(_v[_k], _v[_k + 1], _v[_k + 2]));
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
static void CopyVia(List<List<Vector3i>> _src, List<List<Vector3i>> _dst)
|
||||
{
|
||||
_dst.Clear();
|
||||
foreach (List<Vector3i> l in _src)
|
||||
{
|
||||
_dst.Add(new List<Vector3i>(l));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Списки черепов - вровень с нитями (старые записи без черепов, правки списков нитей).</summary>
|
||||
void AlignVia()
|
||||
{
|
||||
while (FromVia.Count < ThreadFrom.Count) FromVia.Add(new List<Vector3i>());
|
||||
while (FromVia.Count > ThreadFrom.Count) FromVia.RemoveAt(FromVia.Count - 1);
|
||||
while (OutVia.Count < ThreadOut.Count) OutVia.Add(new List<Vector3i>());
|
||||
while (OutVia.Count > ThreadOut.Count) OutVia.RemoveAt(OutVia.Count - 1);
|
||||
if (!HasThreadTo)
|
||||
{
|
||||
ToVia.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Read(PooledBinaryReader _br, StreamModeRead _eStreamMode)
|
||||
@@ -298,6 +366,29 @@ namespace NecromancerTome
|
||||
}
|
||||
Sample = ItemStack.Empty.Clone().Read(_br);
|
||||
}
|
||||
FromVia.Clear();
|
||||
OutVia.Clear();
|
||||
ToVia.Clear();
|
||||
if (version >= 5)
|
||||
{
|
||||
int n = _br.ReadInt32();
|
||||
int[] v = new int[n];
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
v[i] = _br.ReadInt32();
|
||||
}
|
||||
int k = 0;
|
||||
for (int i = 0; i < ThreadFrom.Count; i++)
|
||||
{
|
||||
FromVia.Add(TakeVia(v, ref k));
|
||||
}
|
||||
ToVia.AddRange(TakeVia(v, ref k));
|
||||
for (int i = 0; i < ThreadOut.Count; i++)
|
||||
{
|
||||
OutVia.Add(TakeVia(v, ref k));
|
||||
}
|
||||
}
|
||||
AlignVia();
|
||||
_threadsDirty = true;
|
||||
if (version > SaveVersion)
|
||||
{
|
||||
@@ -337,7 +428,10 @@ namespace NecromancerTome
|
||||
}
|
||||
SyncGenie(false);
|
||||
SyncThreads(_world);
|
||||
Work(_world);
|
||||
if (!CinerariumLoad.Tide)
|
||||
{
|
||||
Work(_world);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
@@ -348,39 +442,54 @@ namespace NecromancerTome
|
||||
GameObject _threadView;
|
||||
float _nextThreadCheck;
|
||||
|
||||
public bool AddThreadFrom(Vector3i _box)
|
||||
/// <summary>_via - черепа-ретрансляторы по порядку от урны к ящику (null - прямая нить). Нить к тому же ящику -
|
||||
/// заменяет прежнюю (новый путь).</summary>
|
||||
public bool AddThreadFrom(Vector3i _box, List<Vector3i> _via = null)
|
||||
{
|
||||
if (ThreadFrom.Contains(_box))
|
||||
AlignVia();
|
||||
int i = ThreadFrom.IndexOf(_box);
|
||||
if (i < 0)
|
||||
{
|
||||
return true;
|
||||
if (ThreadFrom.Count >= CinerariumThreads.MaxFrom)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ThreadFrom.Add(_box);
|
||||
FromVia.Add(new List<Vector3i>());
|
||||
i = ThreadFrom.Count - 1;
|
||||
}
|
||||
if (ThreadFrom.Count >= CinerariumThreads.MaxFrom)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ThreadFrom.Add(_box);
|
||||
FromVia[i] = _via != null ? new List<Vector3i>(_via) : new List<Vector3i>();
|
||||
ThreadsChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetThreadTo(Vector3i _box)
|
||||
public void SetThreadTo(Vector3i _box, List<Vector3i> _via = null)
|
||||
{
|
||||
HasThreadTo = true;
|
||||
ThreadTo = _box;
|
||||
ToVia.Clear();
|
||||
if (_via != null)
|
||||
{
|
||||
ToVia.AddRange(_via);
|
||||
}
|
||||
ThreadsChanged();
|
||||
}
|
||||
|
||||
public bool AddThreadOut(Vector3i _target)
|
||||
public bool AddThreadOut(Vector3i _target, List<Vector3i> _via = null)
|
||||
{
|
||||
if (ThreadOut.Contains(_target))
|
||||
AlignVia();
|
||||
int i = ThreadOut.IndexOf(_target);
|
||||
if (i < 0)
|
||||
{
|
||||
return true;
|
||||
if (ThreadOut.Count >= CinerariumThreads.MaxFrom)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ThreadOut.Add(_target);
|
||||
OutVia.Add(new List<Vector3i>());
|
||||
i = ThreadOut.Count - 1;
|
||||
}
|
||||
if (ThreadOut.Count >= CinerariumThreads.MaxFrom)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ThreadOut.Add(_target);
|
||||
OutVia[i] = _via != null ? new List<Vector3i>(_via) : new List<Vector3i>();
|
||||
ThreadsChanged();
|
||||
return true;
|
||||
}
|
||||
@@ -390,6 +499,9 @@ namespace NecromancerTome
|
||||
int n = ThreadFrom.Count + (HasThreadTo ? 1 : 0) + ThreadOut.Count;
|
||||
ThreadFrom.Clear();
|
||||
ThreadOut.Clear();
|
||||
FromVia.Clear();
|
||||
OutVia.Clear();
|
||||
ToVia.Clear();
|
||||
HasThreadTo = false;
|
||||
if (n > 0)
|
||||
{
|
||||
@@ -398,14 +510,41 @@ namespace NecromancerTome
|
||||
return n;
|
||||
}
|
||||
|
||||
public int RemoveThreadsTo(Vector3i _box)
|
||||
/// <summary>Снять нити, у которых конец (_end) или череп на пути (_via) - в клетке _p.</summary>
|
||||
int RemoveWhere(Vector3i _p, bool _end, bool _via)
|
||||
{
|
||||
int n = ThreadFrom.RemoveAll(p => p == _box) + ThreadOut.RemoveAll(p => p == _box);
|
||||
if (HasThreadTo && ThreadTo == _box)
|
||||
AlignVia();
|
||||
int n = 0;
|
||||
for (int i = ThreadFrom.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_end && ThreadFrom[i] == _p || _via && FromVia[i].Contains(_p))
|
||||
{
|
||||
ThreadFrom.RemoveAt(i);
|
||||
FromVia.RemoveAt(i);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
for (int i = ThreadOut.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_end && ThreadOut[i] == _p || _via && OutVia[i].Contains(_p))
|
||||
{
|
||||
ThreadOut.RemoveAt(i);
|
||||
OutVia.RemoveAt(i);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (HasThreadTo && (_end && ThreadTo == _p || _via && ToVia.Contains(_p)))
|
||||
{
|
||||
HasThreadTo = false;
|
||||
ToVia.Clear();
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
public int RemoveThreadsTo(Vector3i _box)
|
||||
{
|
||||
int n = RemoveWhere(_box, true, false);
|
||||
if (n > 0)
|
||||
{
|
||||
ThreadsChanged();
|
||||
@@ -413,6 +552,53 @@ namespace NecromancerTome
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>Снять нити, идущие через череп _skull (ЛКМ ножом по черепу; череп убрали).</summary>
|
||||
public int RemoveThreadsVia(Vector3i _skull)
|
||||
{
|
||||
int n = RemoveWhere(_skull, false, true);
|
||||
if (n > 0)
|
||||
{
|
||||
ThreadsChanged();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>Все черепы на нитях урны в клетках, где их больше нет (чанк загружен), - нити через них снимаются.</summary>
|
||||
bool DropBrokenRelays(World _world)
|
||||
{
|
||||
AlignVia();
|
||||
bool gone = false;
|
||||
foreach (List<Vector3i> l in AllVia())
|
||||
{
|
||||
foreach (Vector3i p in l)
|
||||
{
|
||||
if (_world.GetChunkFromWorldPos(p) != null && !CinerariumThreads.IsRelay(_world.GetBlock(p).Block))
|
||||
{
|
||||
if (RemoveWhere(p, false, true) > 0)
|
||||
{
|
||||
Debug.Log("[NecromancerTome] Cinerarium: череп-ретранслятор " + p + " убран - нить урны " + ToWorldPos() + " порвалась");
|
||||
gone = true;
|
||||
}
|
||||
return gone; // списки изменились - остальное в следующей проверке
|
||||
}
|
||||
}
|
||||
}
|
||||
return gone;
|
||||
}
|
||||
|
||||
IEnumerable<List<Vector3i>> AllVia()
|
||||
{
|
||||
foreach (List<Vector3i> l in FromVia)
|
||||
{
|
||||
yield return l;
|
||||
}
|
||||
yield return ToVia;
|
||||
foreach (List<Vector3i> l in OutVia)
|
||||
{
|
||||
yield return l;
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadsChanged()
|
||||
{
|
||||
_threadsDirty = true;
|
||||
@@ -427,22 +613,19 @@ namespace NecromancerTome
|
||||
if (_world != null && ConnectionManager.Instance.IsServer && HasThreads && !IsZoneless && _logistKnown)
|
||||
{
|
||||
Debug.Log("[NecromancerTome] Cinerarium: нити у обычной урны " + ToWorldPos() + " сняты - нити только у логиста и рабочего");
|
||||
ThreadFrom.Clear();
|
||||
ThreadOut.Clear();
|
||||
HasThreadTo = false;
|
||||
ThreadsChanged();
|
||||
ClearThreads();
|
||||
}
|
||||
if (_world != null && ConnectionManager.Instance.IsServer && HasThreads && Time.time >= _nextThreadCheck)
|
||||
{
|
||||
_nextThreadCheck = Time.time + 2f;
|
||||
bool gone = false;
|
||||
bool gone = DropBrokenRelays(_world);
|
||||
foreach (Vector3i p in ThreadFrom.ToArray())
|
||||
{
|
||||
if (!CinerariumThreads.Resolve(_world, p, out Vector3i _, out TEFeatureCinerarium u) || u != null)
|
||||
{
|
||||
if (_world.GetChunkFromWorldPos(p) != null) // чанк не загружен - не снимать
|
||||
{
|
||||
ThreadFrom.Remove(p);
|
||||
RemoveWhere(p, true, false);
|
||||
gone = true;
|
||||
}
|
||||
}
|
||||
@@ -451,6 +634,7 @@ namespace NecromancerTome
|
||||
_world.GetChunkFromWorldPos(ThreadTo) != null)
|
||||
{
|
||||
HasThreadTo = false;
|
||||
ToVia.Clear();
|
||||
gone = true;
|
||||
}
|
||||
foreach (Vector3i p in ThreadOut.ToArray())
|
||||
@@ -458,7 +642,7 @@ namespace NecromancerTome
|
||||
bool box = CinerariumThreads.Resolve(_world, p, out Vector3i _, out TEFeatureCinerarium u3) && u3 == null;
|
||||
if (!box && CinerariumThreads.StationAt(_world, p) == null && _world.GetChunkFromWorldPos(p) != null)
|
||||
{
|
||||
ThreadOut.Remove(p);
|
||||
RemoveWhere(p, true, false);
|
||||
gone = true;
|
||||
}
|
||||
}
|
||||
@@ -490,18 +674,19 @@ namespace NecromancerTome
|
||||
_threadView.AddComponent<CinerariumThreadView>();
|
||||
}
|
||||
Vector3i me = ToWorldPos();
|
||||
AlignVia();
|
||||
List<(Vector3 a, Vector3 b)> pairs = new List<(Vector3, Vector3)>();
|
||||
foreach (Vector3i p in ThreadFrom)
|
||||
for (int i = 0; i < ThreadFrom.Count; i++)
|
||||
{
|
||||
pairs.Add((CinerariumThreads.BoxPoint(p, me), CinerariumThreads.UrnPoint(me)));
|
||||
CinerariumThreads.AddPath(pairs, me, FromVia[i], ThreadFrom[i], true);
|
||||
}
|
||||
if (HasThreadTo)
|
||||
{
|
||||
pairs.Add((CinerariumThreads.UrnPoint(me), CinerariumThreads.BoxPoint(ThreadTo, me)));
|
||||
CinerariumThreads.AddPath(pairs, me, ToVia, ThreadTo, false);
|
||||
}
|
||||
foreach (Vector3i p in ThreadOut)
|
||||
for (int i = 0; i < ThreadOut.Count; i++)
|
||||
{
|
||||
pairs.Add((CinerariumThreads.UrnPoint(me), CinerariumThreads.BoxPoint(p, me)));
|
||||
CinerariumThreads.AddPath(pairs, me, OutVia[i], ThreadOut[i], false);
|
||||
}
|
||||
_threadView.GetComponent<CinerariumThreadView>().Build(pairs);
|
||||
}
|
||||
@@ -543,9 +728,9 @@ namespace NecromancerTome
|
||||
_nextWork = Time.time + CinerariumWork.StepSeconds;
|
||||
return;
|
||||
}
|
||||
if (Time.time < _nextWork)
|
||||
if (Time.time < _nextWork || !CinerariumLoad.TryTakeStep())
|
||||
{
|
||||
return;
|
||||
return; // не время или бюджет кадра на дела духов выбран - в следующем кадре
|
||||
}
|
||||
_nextWork = Time.time + CinerariumWork.StepSeconds;
|
||||
Vector3i? at = CinerariumWork.Step(_world, this, out bool stuck, true);
|
||||
@@ -691,7 +876,15 @@ namespace NecromancerTome
|
||||
public void SyncGenie(bool _sound)
|
||||
{
|
||||
SyncGlow();
|
||||
if (HasSkull && _genie == null)
|
||||
// Прилив (17.10: «думаю всех некротехнологов нужно прятать во время прилива... В прилив работа тоже стоит. Урны светятся
|
||||
// как с останками, но духи исчезают»): плотность некроэнергии в воздухе сравнялась с плотностью духа - вне урны он не
|
||||
// держит форму. Свечение урны - по черепу, как обычно; утром дух появляется сам.
|
||||
bool tide = CinerariumLoad.Tide;
|
||||
if (tide && _genie != null)
|
||||
{
|
||||
RemoveGenie(false);
|
||||
}
|
||||
if (HasSkull && _genie == null && !tide)
|
||||
{
|
||||
int logist = 0;
|
||||
if (IsLogist)
|
||||
|
||||
Reference in New Issue
Block a user